| applyTo | **/*.yml,**/*.yaml |
|---|
- YAML Ain't Markup Language
- Human-readable data serialization format
- Superset of JSON (all JSON is valid YAML)
- Three basic data structures: mappings (hashes/dictionaries), sequences (arrays/lists), scalars (strings/numbers)
- Readable: Easy for humans to read and write
- Portable: Works across programming languages
- Expressive: Supports complex data structures
- Minimal: Low syntax overhead
- Preferred:
.yaml(recommended by yaml.info and the YAML specification) - Alternative:
.yml(widely used, but.yamlis unambiguous) - Be consistent within a project — pick one and stick with it
- Use UTF-8 encoding for all YAML files
- Do not use a BOM (Byte Order Mark)
- Always end YAML files with a single newline character
- yamllint enforces this via the
new-line-at-end-of-filerule
- Prefer UNIX-style line endings (
\n) - Avoid DOS-style (
\r\n) unless your toolchain requires it - yamllint
new-linesrule can enforce this
# Explicitly declare the YAML version
%YAML 1.2
---
key: value| Feature | YAML 1.1 | YAML 1.2 |
|---|---|---|
| Truthy values | yes, no, on, off, y, n are booleans |
Only true, false, True, False, TRUE, FALSE |
| Octal notation | 0777 (leading zero) |
0o777 (explicit 0o prefix) |
| Sexagesimals | 1:30 parsed as 90 |
Treated as string |
| Slash escaping | \/ is invalid |
\/ is valid (JSON compatibility) |
- Recommendation: Write YAML as if targeting 1.2 — use
true/false(notyes/no), use0ofor octals, and quote ambiguous values - Many parsers (e.g., PyYAML) still default to YAML 1.1 behaviour — be aware of this when choosing a library
# DANGEROUS — allows arbitrary code execution
data = yaml.load(file, Loader=yaml.FullLoader)
# SAFE — only permits basic YAML types
data = yaml.safe_load(file)# These tags can execute arbitrary code in some parsers!
# NEVER accept untrusted YAML containing these:
malicious: !!python/object/apply:os.system ['rm -rf /']
also_bad: !!python/object:__main__.Exploit {}- Always use
safe_load/SafeLoader(Python),YAML.safe_load(Ruby), or equivalent safe APIs - Never deserialize YAML from untrusted sources without sanitisation
- Disable custom tags and constructors in production environments
- Treat YAML like code — review it for injection risks in CI/CD pipelines
# BAD — hardcoded secrets
database:
password: SuperSecret123!
# GOOD — use environment variable references or external secret stores
database:
password: ${DB_PASSWORD}- Never commit secrets, tokens, or passwords in YAML files
- Use environment variables, vault references, or encrypted secret stores
- Add patterns like
*secret*,*password*,*token*to.gitignoreor use git-secrets
# Correct - using 2 spaces
parent:
child:
grandchild: value
# Incorrect - using tabs
parent:
child: # TAB characters will cause errors
grandchild: value- Standard: Use 2 spaces per indentation level (most common)
- Alternative: 4 spaces (less common, but acceptable if consistent)
- NEVER mix: Always use the same indentation width throughout a file
# Good - consistent 2-space indentation
root:
level1:
level2:
level3: value
# Bad - mixed indentation
root:
level1:
level2: # 4 spaces instead of 2
level3: value # Back to 2 spaces# Correct - list items at same level as parent
items:
- first
- second
- third
# Also correct - inline
items: [first, second, third]
# Nested lists
outer:
- item1
- item2
- nested:
- subitem1
- subitem2The YAML specification allows two ways to indent sequences inside a mapping:
# Style 1: Indented sequences (default, most common)
mapping:
- item1
- item2
# Style 2: Zero-indented sequences (the dash IS the indentation)
mapping:
- item1
- item2- The YAML creators recommend zero-indented sequences, but indented sequences are more widely used
- Be consistent within a project — yamllint's
indent-sequencesoption enforces this - Recommendation: use indented sequences (Style 1) for clarity
# Simple key-value
key: value
name: John Doe
age: 30
# Nested mappings
person:
name: John Doe
age: 30
address:
street: 123 Main St
city: Springfield# Preferred - lowercase with underscores (snake_case)
database_connection: localhost
max_retry_count: 3
# Also acceptable - camelCase
databaseConnection: localhost
maxRetryCount: 3
# Acceptable - kebab-case
database-connection: localhost
max-retry-count: 3
# Less readable - PascalCase (avoid unless required)
DatabaseConnection: localhost
MaxRetryCount: 3# When keys contain special characters
? "key with spaces"
: value
? [complex, key]
: value
# Better: avoid complex keys when possible
key_with_underscores: value# Simple list
fruits:
- apple
- banana
- orange
# List of mappings
users:
- name: Alice
role: admin
- name: Bob
role: user# Inline list
fruits: [apple, banana, orange]
# Inline mapping
user: {name: Alice, role: admin}
# Mixed
users: [{name: Alice, role: admin}, {name: Bob, role: user}]- Block style: Use for multi-item lists, better readability
- Flow style: Use for short lists (1-3 items), or when space is limited
# Simple strings don't need quotes
name: John Doe
message: Hello World
# Be careful with special characters
# These need quotes:
special: "value: with colon"
numbers: "123" # If you want it as string, not number# Single quotes - literal (no escape sequences)
message: 'This is a string'
escaped: 'Use '' for a single quote'
# Double quotes - allow escape sequences
message: "Line 1\nLine 2" # \n creates newline
path: "C:\\Users\\Name" # \\ for backslash
unicode: "Unicode: \u0041" # \u for unicode# Pipe | preserves newlines
script: |
#!/bin/bash
echo "Line 1"
echo "Line 2"
echo "Line 3"
# Result: "#!/bin/bash\necho \"Line 1\"\necho \"Line 2\"\necho \"Line 3\"\n"# Greater-than > folds newlines into spaces
description: >
This is a long description
that spans multiple lines
but will be joined into
a single line.
# Result: "This is a long description that spans multiple lines but will be joined into a single line.\n"# Default - keep final newline
text: |
content
# Strip final newlines: |-
text: |-
content
# Keep all final newlines: |+
text: |+
content
# Must quote
colon_value: "value: with colon"
hash_value: "value # with hash"
at_value: "@value starting with @"
backtick_value: "`value with backticks"
boolean_string: "true" # To prevent interpretation as boolean
number_string: "123" # To prevent interpretation as number
# No need to quote
simple: value
with_spaces: this is fine
with-dashes: also-fine
with_underscores: also_fineThese characters require quoting when they appear as the first character:
!(tag),&(anchor),*(alias)-followed by space (block sequence entry):followed by space (block mapping entry)?followed by space (explicit mapping key){,},[,](flow collection indicators),(flow entry separator)#(comment),|,>(block scalars)@,`(reserved characters)",'(quote characters)%(directive indicator)
:(colon followed by space) — mapping separator#(space followed by hash) — starts a comment- In flow collections, also forbidden:
[,],{,},,
# Integers
integer: 42
negative: -17
octal: 0o14 # Octal notation
hexadecimal: 0x1A # Hex notation
# Floats
float: 3.14159
scientific: 1.23e+3
infinity: .inf
not_a_number: .nan
# As strings (quoted)
version: "1.0"
port: "8080"# True values
enabled: true
enabled: True
enabled: TRUE
enabled: yes
enabled: Yes
enabled: on
# False values
disabled: false
disabled: False
disabled: FALSE
disabled: no
disabled: No
disabled: off
# Recommended: use lowercase true/false for clarity
recommended_true: true
recommended_false: false# Explicit null
value: null
value: Null
value: NULL
value: ~
# Empty value (also null)
value:
# Recommended: use null for clarity
recommended:Caution: Empty values create implicit nulls that may cause unexpected behaviour. yamllint's empty-values rule can forbid these.
# YAML automatically parses ISO-formatted dates!
release: 2024-01-15 # Parsed as a date object, NOT a string
version: 1.0 # Parsed as float 1.0, NOT string "1.0"
timestamp: 2024-01-15T10:30:00Z # Parsed as datetime
# Quote to keep as strings
release: "2024-01-15"
version: "1.0"- Be especially careful with version numbers —
1.0becomes float,1.10becomes1.1 - Always quote values that look like dates or version numbers if you need strings
- yamllint's
quoted-stringsrule withextra-requiredcan enforce quoting for patterns like dates
# This is a comment
key: value # Inline comment
# Multi-line comment block
# Line 1 of comment
# Line 2 of comment
key: value# Good - explain WHY, not WHAT
# Retry count increased due to network instability in production
max_retries: 5
# Bad - states the obvious
# This sets the max retries
max_retries: 5
# Section headers
# ============================================================
# Database Configuration
# ============================================================
database:
host: localhost
port: 5432# Define anchor with &
defaults: &default_settings
timeout: 30
retries: 3
# Reference with *
development:
<<: *default_settings
host: dev.example.com
production:
<<: *default_settings
host: prod.example.com
timeout: 60 # Override specific value# Base configuration
base: &base
name: Base
version: 1.0
# Merge and extend
extended:
<<: *base
description: Extended configuration
version: 2.0 # Overrides base version
# Result of extended:
# name: Base
# version: 2.0
# description: Extended configurationdefault_timeouts: &timeouts
connect_timeout: 5
read_timeout: 30
default_retries: &retries
max_retries: 3
retry_delay: 1
service:
<<: [*timeouts, *retries]
host: example.com# Simple document
key: value
another: value---
# Document 1
name: First Document
value: 123
---
# Document 2
name: Second Document
value: 456
...# --- marks document start (optional for single document)
---
content: here
# ... marks document end (optional)
...---
application:
name: MyApp
version: 1.0.0
database:
host: localhost
port: 5432
credentials:
username: admin
password: secure_password
logging:
level: info
outputs:
- console
- file
file:
path: /var/log/app.log
max_size: 10MB---
name: Build Pipeline
trigger:
branches:
include:
- main
- develop
stages:
- stage: Build
jobs:
- job: BuildJob
pool:
vmImage: 'ubuntu-latest'
steps:
- task: PowerShell@2
displayName: 'Run Tests'
inputs:
targetType: 'inline'
script: |
Invoke-Pester -Output Detailed---
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: example
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:# String (when ambiguous)
version: !!str 1.0
port: !!str 8080
# Integer
count: !!int 42
# Float
price: !!float 19.99
# Boolean
enabled: !!bool true
# Null
value: !!null
# Binary
picture: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5- When the intended type might be ambiguous
- When interfacing with strongly-typed systems
- When you need to ensure a specific interpretation
# The set type uses ? for unique items
colours: !!set
? red
? green
? blue
# Equivalent to a mapping with null values
colours_equivalent:
red: null
green: null
blue: null- ✅ Use 2-space indentation consistently
- ✅ Use lowercase
true/falsefor booleans - ✅ Quote strings that contain special characters
- ✅ Use block style for lists (better readability)
- ✅ Add comments to explain WHY, not WHAT
- ✅ Use anchors and aliases to avoid repetition
- ✅ Keep lines under 100 characters when possible
- ✅ Use meaningful key names
- ✅ Validate YAML with a linter
- ✅ Use
---at document start for multi-document files
- ❌ Never use tabs for indentation
- ❌ Don't mix indentation widths
- ❌ Don't over-use flow style (less readable)
- ❌ Don't use complex keys unless necessary
- ❌ Don't leave trailing whitespace
- ❌ Don't trust unquoted strings with special characters
- ❌ Don't use deprecated YAML 1.0/1.1 syntax
- ❌ Don't repeat configuration (use anchors instead)
- ❌ Don't use
yaml.load()— always usesafe_load()or equivalent - ❌ Don't commit secrets, tokens, or passwords in YAML files
- ❌ Don't rely on
yes/no/on/offas booleans — usetrue/false - ❌ Don't leave version numbers unquoted (
1.0→"1.0") - ❌ Don't use
.ymland.yamlextensions interchangeably in the same project
# Wrong - will cause parse error
message: Value: with colon
# Right
message: "Value: with colon"# Wrong - inconsistent indentation
parent:
child1: value
child2: value # Too much indentation
# Right
parent:
child1: value
child2: value# Wrong - contains tabs (invisible here but causes errors)
key:value
# Right - uses spaces
key: value# Interpreted as boolean true
norway: no # !!!
# Interpreted as string "no"
norway: "no"
# Interpreted as number
version: 1.0
# Interpreted as string "1.0"
version: "1.0"# Wrong - missing space after colon
key:value
# Right
key: value# YAML 1.1: leading zero means octal!
file_mode: 0644 # Interpreted as decimal 420, not 644!
beijing_code: 010 # Interpreted as decimal 8, not 10!
# Fix: quote it
file_mode: "0644"
beijing_code: "010"
# YAML 1.2: only 0o prefix is octal
octal_value: 0o644 # Explicit octal# These are silently parsed as dates, not strings!
release: 2024-01-15 # Date object!
country_code: NO # Boolean false in YAML 1.1! (Norway problem)
# Fix: quote them
release: "2024-01-15"
country_code: "NO"# These are all null, which may not be intended
name:
value: # also null
list:
- # null item in list
# If you mean empty string, use quotes
name: ""# @ and ` (backtick) are reserved — always quote
handle: "@username"
command: "`ls -la`"# The << merge key inserts ALL keys from the anchor.
# Existing keys in the mapping take precedence.
defaults: &defaults
timeout: 30
retries: 3
service:
<<: *defaults
timeout: 60 # Overrides the anchor's value (OK)
# retries: 3 is silently inherited — document this!# Use online validators:
# - https://www.yamllint.com/
# - https://yamlchecker.com/
# Or command-line tools:
# yamllint file.yaml
# python -c "import yaml; yaml.safe_load(open('file.yaml'))"# .yamllint
---
extends: default
rules:
line-length:
max: 120
level: warning
indentation:
spaces: 2
indent-sequences: true
comments:
min-spaces-from-content: 2
truthy:
allowed-values: ['true', 'false']
check-keys: trueAll available yamllint rules:
| Rule | Purpose |
|---|---|
anchors |
Forbid undeclared aliases, duplicated anchors, or unused anchors |
braces |
Control use/spacing of flow mappings { } |
brackets |
Control use/spacing of flow sequences [ ] |
colons |
Enforce spacing around colons (0 before, 1 after) |
commas |
Enforce spacing around commas (0 before, 1 after) |
comments |
Require space after #, min spacing from content |
comments-indentation |
Force comments to be indented like surrounding content |
document-end |
Require or forbid ... document end marker |
document-start |
Require or forbid --- document start marker |
empty-lines |
Limit consecutive blank lines (default max 2) |
empty-values |
Forbid empty values that create implicit nulls |
float-values |
Require numeral before decimal, forbid NaN/Inf/scientific |
hyphens |
Control spacing after hyphens (max 1 space) |
indentation |
Enforce indentation width and sequence indentation style |
key-duplicates |
Prevent duplicate keys in mappings |
key-ordering |
Enforce alphabetical ordering of keys (optional) |
line-length |
Limit line length (default 80, with non-breakable-words exception) |
new-line-at-end-of-file |
Require trailing newline at end of file |
new-lines |
Enforce line ending type (unix or dos) |
octal-values |
Forbid implicit (010) or explicit (0o10) octal values |
quoted-strings |
Require/forbid quoting, enforce quote style |
trailing-spaces |
Forbid trailing whitespace on lines |
truthy |
Forbid ambiguous boolean values (yes, no, on, off) |
# Disable a specific rule for the next line
# yamllint disable-line rule:line-length
very_long_value: this line is intentionally over the limit because it contains a URL or similar content
# Disable a rule for a block
# yamllint disable rule:truthy
on: value # Would normally trigger truthy warning
# yamllint enable rule:truthy
# Disable all rules for a line
# yamllint disable-line
anything: goes here---
BuildWorkflow:
'.':
- build
- test
build:
- Clean
- Build_Module_ModuleBuilder
- Build_NestedModules_ModuleBuilder
- Create_changelog_release_output
test:
- Pester_Tests_Stop_On_Fail
- Pester_if_Code_Coverage_Under_Threshold
ModuleBuildTasks:
Sampler:
- '*.build.Sampler.ib.tasks'---
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
buildConfiguration: 'Release'
steps:
- task: PowerShell@2
inputs:
targetType: 'inline'
script: Write-Host "Building..."- ✅ Use
.yamlfile extension consistently - ✅ UTF-8 encoding, no BOM, UNIX line endings
- ✅ End every file with a single newline
- ✅ Spaces only, never tabs
- ✅ Consistent 2-space indentation throughout the file
- ✅ Quote strings containing special characters or reserved sequences
- ✅ Quote values that look like dates (
"2024-01-15"), versions ("1.0"), or booleans ("yes") - ✅ Use lowercase
true/falsefor booleans — neveryes/no/on/off - ✅ Use
nullexplicitly instead of empty values - ✅ Comment WHY, not WHAT
- ✅ Use anchors and aliases to avoid repetition (DRY)
- ✅ Use
---document start marker; use...end marker in multi-document files - ✅ Use
safe_load/SafeLoader— neveryaml.load()with untrusted input - ✅ Never commit secrets, tokens, or passwords in YAML files
- ✅ Validate with yamllint before committing
- ✅ Enable
truthyrule to catch ambiguous booleans - ✅ Keep lines under 120 characters (80 preferred)
- ✅ Use block style for readability; flow style only for short inline structures
- ✅ Be explicit when ambiguous (use quotes or type tags)
- ✅ Use consistent key naming convention (snake_case preferred)