Chapter 27Lesson 02~330 minutes

Security Policies, Scan Execution, Pipeline Execution, Approval Policies, and Compliance Controls: Guided Hands-On Workflow and Core Operations

Author and evaluate realistic security-policy fixtures on a Free-compatible path, then map the same evidence to optional disposable Ultimate policy enforcement without touching real governance.

Hands-onPolicy fixturesViolation evidenceFree pathOptional Ultimate

Learning objectives

  • Create a disposable policy-fixture repository with realistic scan and pipeline execution policy YAML.
  • Predict linking, inheritance, and policy scope before evaluating any fixture.
  • Simulate one policy violation and capture a structured denial/required-action record.
  • Map fixture evidence to optional Ultimate Policies, pipeline, MR, and audit surfaces.
  • Perform a challenge that selects the correct GitLab control rather than copying a sequence.
Availability baseline — verified 2026-08-22 against GitLab 19.3. Security policies (scan execution, pipeline execution, merge request approval, scheduled pipeline execution, vulnerability management) and security policy projects are currently Ultimate across GitLab.com, Self-Managed, and Dedicated. Compliance frameworks are Premium/Ultimate; using frameworks as policy-enforcement scope and broader security/compliance controls is an Ultimate operating model. Instance-wide compliance and security policy groups are Ultimate on Self-Managed/Dedicated and administrator-controlled. Therefore the mandatory chapter path is a Free-compatible local/CI fixture simulation; live enforcement is optional only when a disposable Ultimate/trial environment and required roles already exist.

1. Disposable scenario and preflight

Create a local repository named gitlab-ch27-policy-lab. The mandatory path needs only Git, Python 3, and a text editor. Nothing is linked to a real GitLab group, no real security policy is changed, and no paid feature is required. If you already have an Ultimate/trial sandbox, you may optionally reproduce a subset in a disposable group after verifying Owner/custom-role permissions.

Do not practice on employer governance. Linking/unlinking policy projects, changing protected branches, granting bypasses, or editing compliance frameworks can alter organization-wide enforcement. The required lab is deliberately local/fixture-based.
mkdir gitlab-ch27-policy-lab && cd gitlab-ch27-policy-lab
git init
git switch -c main
mkdir -p .gitlab/security-policies policy-ci evidence
printf 'training-policy-lab
' > README.md

2. Write the target inventory before the policy

Policy authors should name the intended objects before writing YAML. Create evidence/scope.json:

{
  "linked_unit": "training/platform",
  "projects": [
    {"id": 101, "path": "training/platform/payments-api", "framework": "regulated"},
    {"id": 102, "path": "training/platform/docs-site", "framework": null},
    {"id": 103, "path": "training/platform/legacy-worker", "framework": "exception-review"}
  ],
  "policy_owner": "security-team",
  "application_owners": ["payments-team", "docs-team", "legacy-team"]
}

Prediction 1: if a security policy project were linked at training/platform, all descendants would be candidates through inheritance. A policy that includes only project ID 101 should affect payments-api, not the other two.

3. Author a scan execution policy fixture

The following is a realistic policy shape for enforcing Secret Detection on main for one synthetic project. It is stored as code but is not applied on the Free path.

---
scan_execution_policy:
  - name: Enforce default-branch secret detection
    description: Training fixture - no live enforcement on Free
    enabled: true
    rules:
      - type: pipeline
        branches:
          - main
    actions:
      - scan: secret_detection
    policy_scope:
      projects:
        including:
          - id: 101

Scan execution policies use GitLab security analyzers. They are convenient for standardized scans but intentionally less customizable than pipeline execution policies. Never put credentials in policy variables; policy YAML is plaintext Git content.

4. Author a pipeline execution policy fixture

Pipeline execution policies are better for a custom required job. The policy below injects a version-pinned synthetic CI file and uses the current default-safe job-name collision behavior suffix: on_conflict.

---
pipeline_execution_policy:
  - name: Enforce preflight compliance job
    description: Training fixture - external CI file is synthetic
    enabled: true
    pipeline_config_strategy: inject_policy
    content:
      include:
        - project: training/security-policy-ci
          file: policy-ci.yml
          ref: v1.0.0
    suffix: on_conflict
    policy_scope:
      projects:
        including:
          - id: 101
          - id: 102
policy-preflight:
  stage: .pipeline-policy-pre
  script:
    - echo "synthetic compliance check"
    - test -f policy-required.txt

Prediction 2: both project IDs 101 and 102 would receive policy-preflight. Project 103 would not. A user who triggers a live pipeline must be able to read the policy CI configuration or the pipeline can fail to start; current GitLab can grant linked projects read access to the referenced policy configuration without exposing the full project.

5. Inspect the intended merged control before execution

On Free, inspect the fixture deterministically:

git add .
git commit -m "Add Chapter 27 policy fixtures"
git rev-parse HEAD
git show --stat --oneline HEAD
grep -nE 'scan_execution_policy|pipeline_execution_policy|policy_scope|project:|file:|ref:' .gitlab/security-policies/policy.yml
# If split across files, inspect each exact path instead of relying on a broad directory dump.

For the lab, combine the two top-level policy sections into the single canonical .gitlab/security-policies/policy.yml file, then keep policy-ci.yml under policy-ci/. This preserves the same file boundary a real security policy project uses. It also makes separation of duties visible: policy owners change the central policy file; application owners do not. On an Ultimate sandbox, use the policy editor/YAML validation before merging and record the generated merge request SHA.

6. Simulate one policy violation and preserve the exact reason

The synthetic preflight requires policy-required.txt. Evaluate it without creating that file:

set -eu
if test -f policy-required.txt; then
  printf '%s
' 'policy-preflight: PASS'
else
  printf '%s
' 'policy-preflight: BLOCK - policy-required.txt missing' >&2
  exit 27
fi

Record a structured evaluation object instead of replacing the cause with “policy failed”:

{
  "policy_commit": "fixture-sha-27a",
  "target_project_id": 101,
  "target_ref": "main",
  "pipeline_id": 2701,
  "result": "blocked",
  "reason": "policy-preflight failed: policy-required.txt missing",
  "required_action": "add the required marker in this disposable project or approve a documented training exception"
}

On an Ultimate live pipeline, the equivalent evidence is the policy-originated job, its reserved stage, exact log, target commit SHA, and policy commit. For approval policies, preserve the MR policy-violation/required-approval message and scanner-report evidence.

7. Apply the least-destructive repair

Because this is a disposable functional requirement, the correct repair is to satisfy it in the target project, not disable the central policy.

printf 'chapter-27-training
' > policy-required.txt
git add policy-required.txt
git commit -m "Satisfy synthetic policy preflight"
test -f policy-required.txt && echo 'policy-preflight: PASS'

If a legitimate project cannot meet a real requirement, the next step is not an undocumented bypass. Open an exception record with owner, rationale, affected scope, expiry/review date, compensating control, and evidence. GitLab bypass mechanisms should be used only when their semantics match the approved exception.

8. Optional Ultimate live mapping

Only in a disposable Ultimate/trial group:

  1. Inspect Secure → Policies and current security policy project.
  2. Create a disabled/test policy or narrow it to one disposable project first.
  3. Use Configure with a merge request; review the generated policy.yml diff.
  4. Merge and record the policy commit SHA.
  5. Trigger one tiny target pipeline/MR and verify the policy-originated job or approval requirement.
  6. Inspect audit events where available; do not dump access tokens.
  7. Unlink/delete only after capturing cleanup evidence and confirming no other disposable project depends on the policy.

9. Control-selection challenge

Choose the smallest correct control for each requirement before revealing the answers:

Requirement Your choice
Run GitLab Secret Detection on every default-branch pipeline ?
Run an organization-specific artifact-signature verifier before project jobs ?
Require two approvals only when completed security scan evidence violates policy ?
Run a compliance script weekly even without commits ?
Reveal recommended controls

Use scan execution policy; pipeline execution policy; merge request approval policy; scheduled pipeline execution policy, respectively. The choice follows the enforcement object and evaluation timing—not the UI menu name.

10. Cleanup and verify

git status --short
git log --oneline --decorate -3
# Keep only sanitized evidence if needed, then remove the local disposable repository from its parent directory.
# If you used an Ultimate sandbox, verify policy unlink/deletion through UI/API before deleting target projects.

Knowledge check

Why is the policy fixture pinned to ref v1.0.0 instead of main?

A target pipeline cannot read the policy CI file. Is that a scanner finding?

The preflight job fails. Should the first repair be disabling the central policy?

Which policy type creates independent scheduled pipelines with policy CI only?

What must be verified after unlinking a disposable security policy project?

Summary and next step

You now have a causal workflow: inventory scope → author policy intent → predict enforcement → preserve exact violation → least-destructive repair → verify. Lesson 3 turns those mechanics into an architecture and governance decision framework.

Primary sources and version notes

These lessons were finalized against current official GitLab documentation on 2026-08-22. Re-check tier, feature status, policy schema, permission, audit-event, and Self-Managed-version behavior before applying the same design later.

Next lesson

Configuration, Design Choices, and Tradeoffs

Turn policy mechanics into architecture decisions about ownership, scope, enforcement type, exceptions, reliability, and cost.

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.