Chapter 13Lesson 02~285 minutes

CI/CD Variables, Inputs, Secrets, File Variables, Masking, Protection, and Scope: Guided Hands-On Workflow and Core Operations

Use only synthetic data in a disposable GitLab project to inspect variable metadata, prove precedence, handle file variables, compare protected and unprotected refs, and validate typed pipeline inputs.

Disposable labSynthetic valuesProtected refsFile variablePipeline inputsFree path

Learning objectives

  • Inspect variable metadata without revealing any value.
  • Create disposable non-secret variables and prove project-over-YAML precedence.
  • Use a synthetic file variable and verify its temporary-file behavior without logging its contents.
  • Prove protected-variable presence on a protected ref and absence on an unprotected ref using only a boolean signal.
  • Define and validate typed pipeline inputs without putting secrets into source or run parameters.
Availability baseline (verified 2026-08-21; GitLab 19.3 is the latest monthly release, released 2026-08-20). Project and group CI/CD variables, masking, hiding, protection, file variables, pipeline inputs, predefined variables, and OIDC ID tokens are available on Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated unless a narrower note is stated. Instance variables are Self-Managed/Dedicated administrator scope. Group-variable environment scoping is Premium/Ultimate. GitLab's built-in external-secret provider integrations are Premium/Ultimate, while the underlying ID-token/OIDC mechanism is Free. The mandatory chapter path uses only a disposable project, synthetic values, and a no-paid validation/fixture fallback.

1. Disposable scenario and safety boundary

Create a fresh disposable project such as gitlab-ch13-variable-lab. Do not reuse a production project: this lesson intentionally adds and removes variables and relies on the default branch being a protected ref.

Requirement Mandatory path
Tier GitLab Free is sufficient.
Role Maintainer for project variables; Developer may run/push pipelines but cannot manage variables.
Data Every value is synthetic and harmless. Never paste a real token, password, key, certificate, or recovery code.
Runner Tiny optional jobs. If no runner capacity exists, use CI Lint plus the supplied expected-log fixtures.
Protected ref Use the disposable default branch as the protected case; use ch13/unprotected as the unprotected case.
External services None. No cloud account, secret manager, registry, deployment, or Kubernetes resource is required.

2. Preflight: inspect without revealing values

Before adding anything, record the project path, default branch, default-branch protection rule, and whether any group variables are inherited. In Settings → CI/CD → Variables, inspect only keys and metadata. Do not click reveal controls for existing values.

If this is a brand-new disposable project with no pre-existing sensitive variables, glab variable list can help inventory the keys. Do not use glab variable get on real projects because that command prints a value.

glab auth status
# Safe only in the empty/disposable lab project:
glab variable list --output json --jq '.[] | {key, variable_type, protected, masked, hidden, environment_scope}'
git branch --show-current
git rev-parse HEAD

3. Create three harmless lab variables

Create these through the UI or current glab variable set. Because the values are intentionally non-secret, command history is not a concern. Never copy this pattern to a real credential.

glab variable set CH13_PRIORITY "project-setting" --description "Synthetic precedence value"
printf 'profile=checkpoint\nregion=example\n' > ch13-file-fixture.txt
glab variable set CH13_FILE --type file < ch13-file-fixture.txt
glab variable set CH13_PROTECTED "synthetic-protected-marker" --masked --protected

After creation, inspect metadata again. CH13_FILE should report type file; CH13_PROTECTED should report protected and masked. Do not retrieve either value through a read command.

4. Prove precedence with non-sensitive output

Put a lower-precedence default and job value in the repository. The project variable with the same key should win.

variables:
  CH13_PRIORITY: "yaml-default"

precedence_probe:
  variables:
    CH13_PRIORITY: "yaml-job"
  script:
    - printf 'priority=%s\n' "$CH13_PRIORITY"

Expected safe log: priority=project-setting. This value is intentionally public test data. If you remove the project variable later, the job-level YAML value becomes effective. This demonstrates why a developer who edits only the job block can be surprised by inherited/settings variables.

5. Verify file-variable semantics without printing content

file_probe:
  script:
    - test -n "${CH13_FILE:-}"
    - test -f "$CH13_FILE"
    - printf 'file_present=yes\n'
    - printf 'file_bytes=%s\n' "$(wc -c < "$CH13_FILE")"
    - grep -q '^profile=checkpoint$' "$CH13_FILE"
    - printf 'fixture_shape=valid\n' 

The job never prints the file contents. It proves only that the environment variable points to an existing temporary file with the expected benign structure. A common error is printf '%s' "$CH13_FILE" and assuming that prints the file data; it prints the path.

6. Presence-only protected-variable probe

Do not print the protected variable. Record only whether it exists:

protected_probe:
  script:
    - if [ -n "${CH13_PROTECTED:-}" ]; then printf 'protected_present=yes\n'; else printf 'protected_present=no\n'; fi

Commit the CI configuration to the disposable default branch. On the protected default branch, expected output is protected_present=yes. Create ch13/unprotected from the same commit and push it; expected output is protected_present=no. If the project has a setting that permits protected resources in certain MR pipelines, record that explicitly rather than assuming all MRs behave alike.

7. Bind every observation to a commit/ref

git add -- .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "ch13: add synthetic variable probes"
DEFAULT_SHA="$(git rev-parse HEAD)"
printf 'default_sha=%s\n' "$DEFAULT_SHA"
git push origin HEAD

In GitLab, record pipeline ID, source, ref, SHA, and job status. If there is no eligible runner, preserve the pending state plus the validated configuration as the no-compute evidence path.

8. Create the unprotected comparison branch

git switch -c ch13/unprotected
git push -u origin ch13/unprotected
git rev-parse HEAD

Do not change the protected variable. The variable stays the same; only the ref eligibility changes. This isolates causality. Verify that CH13_PRIORITY and the file variable remain available, while CH13_PROTECTED is absent.

9. Add typed pipeline inputs for configuration—not secrets

Inputs belong in the configuration header. Give defaults so automatically created branch/MR pipelines do not fail waiting for user input.

spec:
  inputs:
    profile:
      description: "Synthetic execution profile"
      options: [lab, audit]
      default: lab
    retry_count:
      type: number
      default: 1
---

input_probe:
  script:
    - printf 'profile=%s\n' "$[[ inputs.profile ]]"
    - printf 'retry_count=%s\n' "$[[ inputs.retry_count ]]"

Validate in Pipeline Editor/CI Lint before pushing. For a manual run, select audit and a small number. These are non-sensitive parameters and may safely appear in the configuration/log. Never use a pipeline input field as an ad-hoc password box.

10. No-runner evidence fixture

If no runner is available, compare your expected state with this fixture rather than purchasing compute:

# protected default branch fixture
priority=project-setting
file_present=yes
file_bytes=34
fixture_shape=valid
protected_present=yes

# unprotected branch fixture
priority=project-setting
file_present=yes
file_bytes=34
fixture_shape=valid
protected_present=no

11. Small challenge: choose the right surface

Classify each requirement before editing anything:

Requirement Best starting surface
Operator selects lab or audit Typed pipeline input with options.
One project needs a stable non-secret endpoint Project variable or repository YAML if public configuration is acceptable.
A tool expects a certificate file File-type project variable for a synthetic lab; external secret/file retrieval in production.
A fake marker should exist only on protected refs Protected project variable.
One setting should apply to many projects Group variable, with inheritance review before use.

12. Cleanup and verification

Delete the lab variables by key and verify their metadata no longer appears. Remove the local fixture file. Delete the synthetic branch after capturing the SHA/pipeline evidence. If the whole project was created only for this lab, project deletion is acceptable but is destructive—verify the target project path first.

glab variable delete CH13_PRIORITY
glab variable delete CH13_FILE
glab variable delete CH13_PROTECTED
glab variable list --output json --jq '[.[] | .key]'
rm -f -- ch13-file-fixture.txt
git push origin --delete ch13/unprotected

Knowledge check

Why did the project variable beat the YAML job variable?

What safe evidence proved the file variable worked?

Why did the protected marker disappear on ch13/unprotected?

Why do the inputs have defaults?

What command should you avoid on a real variable if your goal is metadata-only inspection?

Summary

The lab proved four independent mechanisms with synthetic data: precedence decides the effective value, file variables provide paths, protection filters values by ref trust, and inputs provide validated configuration parameters. Every observation was tied to a pipeline/ref/SHA, and cleanup removed the variable state rather than leaving hidden configuration behind.

Official references

Next lesson

Choose the smallest safe configuration mechanism

Lesson 3 turns these mechanics into architecture decisions: input versus variable, project versus group inheritance, protected versus environment scope, and stored value versus OIDC-backed external secret retrieval.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.