Build Parameters, Environment Variables, Build Causes, Schedules, Remote Triggers, and Parameterized Automation: Guided Hands-On Workflow and Core Operations
Build a disposable parameterized maintenance simulation, trigger it manually, by timer, and through the authenticated Remote API, then prove causes, inputs, queue/build identity, validation, and idempotent side effects.
Learning objectives
- Create string, choice, and boolean parameters without using real secrets.
- Build a safe shell step that allowlists values before deriving a target path or side effect.
- Configure a short-lived hashed timer schedule and keep overlapping execution bounded.
- Trigger the job through the authenticated Remote API with an API token and no CSRF bypass.
- Collect build cause, parameter, queue/build, artifact, and simulated-target evidence.
1. Lab scenario and safety boundary
This lab continues the disposable controller/agent model from
earlier chapters. Use a non-production Jenkins controller and a
disposable Linux agent labelled lab-linux. The job
never touches a real server, registry, cloud account, or production
credential. Its only “external” side effect is a marker under
/tmp/jenkins-ch07-maintenance on the disposable agent.
2. Preflight: prove the controller, agent, and API baseline
Record the Jenkins core version, Java version, job namespace, agent label, and the non-secret identity of the API user you will use. Do not log the API token itself.
java -version
printf 'agent=%s\nworkspace=%s\n' "${NODE_NAME:-unknown}" "${WORKSPACE:-unknown}"
# From an authenticated client, a response header from any Jenkins API page includes X-Jenkins.
Confirm the built-in controller node is not used for the job. If no disposable agent exists, create one using the same safe local method from the earlier controller/agent chapter before continuing.
3. Create ch07-maintenance and define parameters
Create a Freestyle project named ch07-maintenance.
Restrict it to the lab-linux label. Enable
This project is parameterized and define:
| Name | Type | Default / choices | Purpose |
|---|---|---|---|
NOTE |
String | training-run |
Harmless free-form evidence; length/characters are validated. |
TARGET |
Choice | dev, staging |
Bounded simulated environment. |
ACTION |
Choice | inspect, rotate-cache |
Read-only or idempotent simulated operation. |
DRY_RUN |
Boolean | checked | Prevents the marker side effect while learning. |
Do not add a password parameter. Real secrets belong in Jenkins Credentials or an external secret provider, not in a course parameter.
4. Add a validation-first shell build step
Add an Execute shell step with the following
script. The script validates all user-controlled values before using
them to derive a path or action. It does not use
eval or construct a shell command from strings.
set -eu
ROOT=/tmp/jenkins-ch07-maintenance
case "$TARGET" in
dev|staging) ;;
*) printf 'Rejected TARGET=%s\n' "$TARGET" >&2; exit 64 ;;
esac
case "$ACTION" in
inspect|rotate-cache) ;;
*) printf 'Rejected ACTION=%s\n' "$ACTION" >&2; exit 64 ;;
esac
case "$NOTE" in
*[!A-Za-z0-9._-]*|'')
printf 'Rejected NOTE: use 1+ characters from A-Z a-z 0-9 . _ -\n' >&2
exit 64
;;
esac
TARGET_DIR="$ROOT/$TARGET"
mkdir -p -- "$TARGET_DIR"
{
printf 'job=%s\n' "$JOB_NAME"
printf 'build=%s\n' "$BUILD_NUMBER"
printf 'url=%s\n' "$BUILD_URL"
printf 'node=%s\n' "$NODE_NAME"
printf 'target=%s\n' "$TARGET"
printf 'action=%s\n' "$ACTION"
printf 'dry_run=%s\n' "$DRY_RUN"
printf 'note=%s\n' "$NOTE"
printf 'time_utc=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} | tee "$WORKSPACE/evidence.txt"
if [ "$ACTION" = inspect ]; then
find "$TARGET_DIR" -maxdepth 1 -type f -printf '%f\n' | sort || true
exit 0
fi
MARKER="$TARGET_DIR/cache-rotated.marker"
if [ "$DRY_RUN" = true ]; then
printf 'DRY RUN: would create %s\n' "$MARKER"
elif [ -f "$MARKER" ]; then
printf 'Idempotent no-op: marker already exists at %s\n' "$MARKER"
else
printf 'created_by=%s#%s\n' "$JOB_NAME" "$BUILD_NUMBER" > "$MARKER"
printf 'created_utc=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$MARKER"
printf 'Created %s\n' "$MARKER"
fi
Add Archive the artifacts with
evidence.txt. This preserves selected non-secret
runtime evidence even if the workspace later disappears.
5. Manual trigger: establish the first cause/input packet
Use Build with Parameters with
TARGET=dev, ACTION=inspect,
DRY_RUN=true, and a safe NOTE. Before
clicking Build, predict the expected build cause, parameter values,
agent label, and the fact that no marker will be created.
After completion, record job full name, build number/URL, cause
shown in the UI, parameters, node name, workspace, console result,
and archived evidence.txt. Do not infer cause from the
NOTE value.
6. Configure a short-lived hashed timer safely
Enable Build periodically with the lab-only schedule:
H/5 * * * *
This uses Jenkins' hash syntax to choose a stable offset every five
minutes. Keep DRY_RUN defaulted to true and leave
concurrent execution disabled. Wait for one timer-fired build if
practical, capture its cause and inputs, then remove the timer when
the lab is complete.
If you cannot wait, configuring and saving the schedule plus inspecting Jenkins' next-run indication still teaches the configuration layer, but the checkpoint lesson later requires observing a real timer cause.
7. Trigger a parameterized build through the authenticated Remote API
Create a disposable Jenkins user with only the permissions needed to read/build this lab job, then create an API token for that user. Keep the token in a local session variable and never paste it into the Jenkins URL.
export JENKINS_URL='http://127.0.0.1:8080'
export JENKINS_USER='ch07-api'
read -r -s -p 'API token: ' JENKINS_API_TOKEN; export JENKINS_API_TOKEN; printf '\n'
curl --fail-with-body --silent --show-error --request POST \
--user "$JENKINS_USER:$JENKINS_API_TOKEN" \
--data-urlencode 'TARGET=dev' \
--data-urlencode 'ACTION=rotate-cache' \
--data-urlencode 'DRY_RUN=false' \
--data-urlencode 'NOTE=api-run-001' \
"$JENKINS_URL/job/ch07-maintenance/buildWithParameters"
API-token authentication is the reason this scripted POST does not need a CSRF crumb. Do not disable CSRF. Depending on Jenkins/plugin details, the exact remote cause payload can differ; inspect the resulting build rather than hard-coding a cause class into your automation.
8. Inspect cause + parameters through the build API
After the API-triggered build starts, query its build record. Use
the build number you observed rather than assuming
lastBuild remains stable on a busy controller.
BUILD=3 # replace with the observed build number
curl --silent --show-error \
--user "$JENKINS_USER:$JENKINS_API_TOKEN" \
"$JENKINS_URL/job/ch07-maintenance/$BUILD/api/json?tree=number,url,queueId,result,actions[causes[*],parameters[name,value]]"
The returned build number, queue ID, cause structure, parameter set,
and result are Jenkins-side evidence. The marker under
/tmp/jenkins-ch07-maintenance/dev is separate
agent-side evidence.
9. Prove idempotency with a second API build
Trigger the same rotate-cache request again with
DRY_RUN=false. The second build must not create a
second side effect; it should report that the marker already exists.
You now have two Jenkins build identities but one simulated target
transition.
10. Reject an unsafe input without exploiting anything
Change NOTE to a value containing a slash such as
../../prod. The validation step must reject it before
any marker path or action is derived. This is a safe demonstration
of fail-closed validation; there is no need to execute a real
command-injection payload.
11. Cleanup and rollback
Disable/remove the timer first. Delete the lab API token from the Jenkins user. Then, only on the disposable agent, verify the exact temporary root and remove it:
ROOT=/tmp/jenkins-ch07-maintenance
[ "$ROOT" = /tmp/jenkins-ch07-maintenance ] || exit 70
find "$ROOT" -maxdepth 2 -type f -print 2>/dev/null || true
rm -rf -- "$ROOT"
Finally delete the ch07-maintenance job only if it was
created solely for this lab. Preserve exported evidence first if you
need it for the checkpoint.
12. Small challenge: identify the failing layer
An API request returns successfully, but no marker appears. List the evidence you would inspect in order: HTTP response/queue identity, build existence, cause/parameters, assigned node, validation output, build result, and finally the marker path. Do not jump directly to “API failed” or delete/recreate the job.
Knowledge check
Why does the lab archive evidence.txt instead of
trusting the workspace?
Workspaces are execution state and may be cleaned or replaced. The archived evidence remains attached to the build record.
Why is DRY_RUN defaulted to true?
The safest default makes an omitted or scheduled input non-destructive until the learner explicitly opts into the bounded simulated side effect.
Why does the script validate TARGET even though it
is a choice parameter?
The executing logic should not make a security or safety decision solely from the UI/configuration representation.
What proves the API request actually changed the simulated target?
The Jenkins build record proves execution; the marker under the guarded target root proves the simulated external state transition.
What should be removed before deleting the lab job?
Disable the schedule, revoke the disposable API token, preserve needed evidence, then clean only the guarded temporary target root.
Official references and version notes
- Jenkins LTS changelog — current LTS release and tested Java configurations.
-
Pipeline Syntax
— parameter, environment, trigger, cron, and
triggeredBysemantics. -
Using a Jenkinsfile
—
env, build identity variables, and parameter access. - Using environment variables — global and stage-scoped environment behavior.
- Handling Environment Variables — security risks from untrusted environment names and values.
- Remote Access API — authenticated build submission and programmatic inspection.
- CSRF Protection — crumbs for scripted clients and the API-token exemption.
-
Jenkins
CauseAPI — the build-cause model used by Jenkins core.
Rechecked on 2026-09-16. Examples target
Jenkins 2.568.3 LTS, which is tested with Java 21
and 25; the disposable lab uses Java 21. The chapter intentionally
relies on Jenkins core/Freestyle parameter, timer, build-cause,
environment, and Remote API behavior so mandatory completion does
not depend on a commercial service or a specialized trigger
plugin. Exact cause payloads and environment variables can vary by
job type and installed plugins, so inspect the controller's own
.../api/ output rather than assuming a fixed payload.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.