yq Session 04: Multi-Document

YAML files can contain multiple documents separated by ---. This session covers multi-doc processing, document selection, and YAML anchors/aliases.

Pre-Session State

  • Can read, filter, and edit YAML

  • Understand -i for in-place edits

  • Can merge documents with *

Setup

cat > /tmp/yq-multi.yaml << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
  name: wazuh
---
apiVersion: v1
kind: Service
metadata:
  name: wazuh-indexer
  namespace: wazuh
spec:
  ports:
    - port: 9200
      targetPort: 9200
  selector:
    app: wazuh-indexer
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: wazuh-indexer
  namespace: wazuh
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wazuh-indexer
EOF

Lesson 1: Document Selection

Concept: documentIndex or di selects which document in a multi-doc file.

Exercise 1.1: Select first document

yq 'select(di == 0)' /tmp/yq-multi.yaml

Output: Only the Namespace document.

Exercise 1.2: Select by kind

yq 'select(.kind == "Service")' /tmp/yq-multi.yaml

Output: Only the Service document.

Exercise 1.3: Count documents

yq '[.] | length' /tmp/yq-multi.yaml

Alternatively, count with a pipeline:

yq -N 'di' /tmp/yq-multi.yaml | tail -1

Lesson 2: Iterate All Documents

Concept: Without select, yq processes all documents.

Exercise 2.1: Get kind from each document

yq '.kind' /tmp/yq-multi.yaml

Output: Namespace, Service, StatefulSet — one per line.

Exercise 2.2: Get metadata from each

yq '.metadata.name' /tmp/yq-multi.yaml

Output: wazuh, wazuh-indexer, wazuh-indexer

Lesson 3: Edit Specific Documents

Concept: Combine select(di == N) with assignment.

Exercise 3.1: Update replicas in the StatefulSet only

yq 'select(di == 2).spec.replicas = 3' /tmp/yq-multi.yaml

Output: Only the StatefulSet has replicas changed. Other docs unchanged.

Exercise 3.2: Add label to all documents

yq '.metadata.labels.managed-by = "yq-drill"' /tmp/yq-multi.yaml

Output: Every document gets the label.

Lesson 4: Anchors and Aliases

Concept: YAML anchors (&name) define reusable blocks. Aliases (*name) reference them.

Exercise 4.1: Create anchored data

cat > /tmp/yq-anchor.yaml << 'EOF'
defaults: &defaults
  timeout: 30
  retries: 3
  tls: true

services:
  vault:
    <<: *defaults
    port: 8200
  consul:
    <<: *defaults
    port: 8500
    timeout: 60
EOF
yq '.' /tmp/yq-anchor.yaml

Output: yq expands aliases by default.

Exercise 4.2: Explode anchors

yq 'explode(.)' /tmp/yq-anchor.yaml

Output: All anchors and aliases replaced with actual values. Useful for debugging "what does this YAML actually resolve to?"

Exercise 4.3: Check a merged value

yq '.services.consul.timeout' /tmp/yq-anchor.yaml

Output: 60 — the override wins over the anchor default of 30.

yq '.services.consul.retries' /tmp/yq-anchor.yaml

Output: 3 — inherited from the anchor.

Lesson 5: Split and Join

Concept: Split multi-doc into separate files, or join files into multi-doc.

Exercise 5.1: Split by document

mkdir -p /tmp/yq-split
yq -s '"/tmp/yq-split/doc-" + $index' /tmp/yq-multi.yaml
ls /tmp/yq-split/

Creates doc-0.yml, doc-1.yml, doc-2.yml.

Exercise 5.2: Evaluate split output

yq '.' /tmp/yq-split/doc-1.yml

Output: Just the Service document.

Summary: What You Learned

Concept Syntax Example

Document index

di

select(di == 0) first doc

Select by field

select(.key == val)

select(.kind == "Service")

Edit one doc

select(di == N).key = val

Change only target document

Explode anchors

explode(.)

Resolve all aliases

Split

-s

yq -s '"prefix-" + $index'

Exercises to Complete

  1. [ ] Get all Service ports across documents

  2. [ ] Add a namespace: monitoring to only the Namespace document

  3. [ ] Create a YAML file with anchors, explode it, compare

  4. [ ] Split yq-multi.yaml, edit one file, recombine with cat

Next Session

Session 05: Infrastructure - Real antora.yml, GitHub Actions, k8s manifests.

Session Log

Timestamp Notes

Start

<Record when you started>

End

<Record when you finished>