0
# Template Engine
1
2
Ansible Core's template engine provides comprehensive Jinja2-based variable substitution, conditional logic, loops, and expression evaluation with Ansible-specific filters, tests, functions, and security controls optimized for automation workflows.
3
4
## Capabilities
5
6
### Template Engine Core
7
8
The main templating engine coordinating Jinja2 processing with Ansible-specific enhancements, variable resolution, and security controls.
9
10
```python { .api }
11
class Templar:
12
"""
13
Main templating engine with Jinja2 integration and Ansible enhancements.
14
15
Attributes:
16
- _loader: DataLoader instance
17
- _variables: Available variables
18
- environment: Jinja2 environment
19
- _fail_on_undefined: Whether to fail on undefined variables
20
- _finalized: Whether templating is finalized
21
"""
22
23
def __init__(self, loader, variables=None):
24
"""
25
Initialize templating engine.
26
27
Parameters:
28
- loader: DataLoader instance
29
- variables: Available variables dictionary
30
"""
31
32
def template(self, variable, convert_bare=False, preserve_trailing_newlines=True,
33
escape_backslashes=True, fail_on_undefined=None, overrides=None,
34
convert_data=True, static_vars=None, cache=True, disable_lookups=False):
35
"""
36
Template a variable with current context.
37
38
Parameters:
39
- variable: Variable to template (string, dict, list, etc.)
40
- convert_bare: Convert bare variables
41
- preserve_trailing_newlines: Keep trailing newlines
42
- escape_backslashes: Escape backslash characters
43
- fail_on_undefined: Fail on undefined variables
44
- overrides: Variable overrides
45
- convert_data: Convert templated data
46
- static_vars: Static variables list
47
- cache: Use template caching
48
- disable_lookups: Disable lookup plugins
49
50
Returns:
51
object: Templated result
52
"""
53
54
def is_template(self, data):
55
"""
56
Check if data contains template expressions.
57
58
Parameters:
59
- data: Data to check
60
61
Returns:
62
bool: True if data contains templates
63
"""
64
65
def set_available_variables(self, variables):
66
"""
67
Set available variables for templating.
68
69
Parameters:
70
- variables: Variables dictionary
71
"""
72
73
def get_available_variables(self):
74
"""
75
Get currently available variables.
76
77
Returns:
78
dict: Available variables
79
"""
80
```
81
82
### Jinja2 Environment
83
84
Customized Jinja2 environment with Ansible-specific filters, tests, globals, and configuration optimized for automation use cases.
85
86
```python { .api }
87
class AnsibleEnvironment:
88
"""
89
Ansible-customized Jinja2 environment with enhanced functionality.
90
91
Provides Ansible-specific filters, tests, and globals while maintaining
92
Jinja2 compatibility and adding security controls.
93
"""
94
95
def __init__(self, *args, **kwargs):
96
"""Initialize Ansible Jinja2 environment"""
97
98
def get_template(self, name, parent=None, globals=None):
99
"""
100
Get template by name with Ansible enhancements.
101
102
Parameters:
103
- name: Template name
104
- parent: Parent template
105
- globals: Template globals
106
107
Returns:
108
Template: Jinja2 template object
109
"""
110
111
class AnsibleContext:
112
"""
113
Template context with Ansible-specific variable resolution and scoping.
114
115
Manages variable inheritance, scoping, and resolution order for
116
templates while providing access to Ansible-specific data.
117
"""
118
```
119
120
### Template Filters
121
122
Ansible-specific Jinja2 filters for data transformation, formatting, and manipulation commonly needed in automation workflows.
123
124
```python { .api }
125
# String and text filters
126
def to_yaml(data, indent=2, default_flow_style=False):
127
"""
128
Convert data to YAML format.
129
130
Parameters:
131
- data: Data to convert
132
- indent: YAML indentation
133
- default_flow_style: Use flow style
134
135
Returns:
136
str: YAML representation
137
"""
138
139
def to_json(data, indent=None, sort_keys=False):
140
"""
141
Convert data to JSON format.
142
143
Parameters:
144
- data: Data to convert
145
- indent: JSON indentation
146
- sort_keys: Sort dictionary keys
147
148
Returns:
149
str: JSON representation
150
"""
151
152
def regex_replace(value, pattern, replacement, ignorecase=False, multiline=False):
153
"""
154
Replace text using regular expressions.
155
156
Parameters:
157
- value: Input string
158
- pattern: Regex pattern
159
- replacement: Replacement string
160
- ignorecase: Case insensitive matching
161
- multiline: Multiline mode
162
163
Returns:
164
str: String with replacements
165
"""
166
167
def regex_search(value, pattern, groups=None, ignorecase=False, multiline=False):
168
"""
169
Search for regex pattern in string.
170
171
Parameters:
172
- value: Input string
173
- pattern: Regex pattern
174
- groups: Groups to extract
175
- ignorecase: Case insensitive matching
176
- multiline: Multiline mode
177
178
Returns:
179
str|list: Match result or groups
180
"""
181
182
def b64encode(data):
183
"""
184
Base64 encode data.
185
186
Parameters:
187
- data: Data to encode
188
189
Returns:
190
str: Base64 encoded string
191
"""
192
193
def b64decode(data):
194
"""
195
Base64 decode data.
196
197
Parameters:
198
- data: Data to decode
199
200
Returns:
201
str: Decoded string
202
"""
203
204
# List and dictionary filters
205
def map(attribute, iterable):
206
"""
207
Extract attribute from list of objects.
208
209
Parameters:
210
- attribute: Attribute name to extract
211
- iterable: List of objects
212
213
Returns:
214
list: Extracted values
215
"""
216
217
def select(test, iterable):
218
"""
219
Filter list items by test condition.
220
221
Parameters:
222
- test: Test condition
223
- iterable: List to filter
224
225
Returns:
226
list: Filtered items
227
"""
228
229
def reject(test, iterable):
230
"""
231
Reject list items by test condition.
232
233
Parameters:
234
- test: Test condition
235
- iterable: List to filter
236
237
Returns:
238
list: Remaining items
239
"""
240
241
def combine(*dicts, recursive=False, list_merge='replace'):
242
"""
243
Combine multiple dictionaries.
244
245
Parameters:
246
- dicts: Dictionaries to combine
247
- recursive: Recursive merging
248
- list_merge: List merge strategy
249
250
Returns:
251
dict: Combined dictionary
252
"""
253
254
# Network and system filters
255
def ipaddr(value, query=''):
256
"""
257
IP address manipulation and validation.
258
259
Parameters:
260
- value: IP address or network
261
- query: Query type (network, host, etc.)
262
263
Returns:
264
str: Processed IP address
265
"""
266
267
def hash(data, hashtype='sha1'):
268
"""
269
Generate hash of data.
270
271
Parameters:
272
- data: Data to hash
273
- hashtype: Hash algorithm
274
275
Returns:
276
str: Hash digest
277
"""
278
279
def password_hash(password, hashtype='sha512', salt=None):
280
"""
281
Generate password hash.
282
283
Parameters:
284
- password: Password to hash
285
- hashtype: Hash algorithm
286
- salt: Salt value
287
288
Returns:
289
str: Password hash
290
"""
291
```
292
293
### Template Tests
294
295
Ansible-specific Jinja2 tests for conditional evaluation and data validation in templates and playbooks.
296
297
```python { .api }
298
# Type and value tests
299
def is_string(value):
300
"""Test if value is string"""
301
302
def is_number(value):
303
"""Test if value is number"""
304
305
def is_boolean(value):
306
"""Test if value is boolean"""
307
308
def is_list(value):
309
"""Test if value is list"""
310
311
def is_dict(value):
312
"""Test if value is dictionary"""
313
314
# Network tests
315
def is_ip(value):
316
"""
317
Test if value is valid IP address.
318
319
Parameters:
320
- value: Value to test
321
322
Returns:
323
bool: True if valid IP address
324
"""
325
326
def is_ipv4(value):
327
"""Test if value is IPv4 address"""
328
329
def is_ipv6(value):
330
"""Test if value is IPv6 address"""
331
332
def is_mac(value):
333
"""Test if value is MAC address"""
334
335
# Version tests
336
def version_compare(value, version, operator='==', strict=False):
337
"""
338
Compare version strings.
339
340
Parameters:
341
- value: Version to compare
342
- version: Comparison version
343
- operator: Comparison operator
344
- strict: Strict version parsing
345
346
Returns:
347
bool: Comparison result
348
"""
349
350
def version(value, version, operator='=='):
351
"""Alias for version_compare"""
352
353
# File and path tests
354
def is_file(path):
355
"""Test if path is regular file"""
356
357
def is_dir(path):
358
"""Test if path is directory"""
359
360
def is_link(path):
361
"""Test if path is symbolic link"""
362
363
def exists(path):
364
"""Test if path exists"""
365
366
# Content tests
367
def match(value, pattern, ignorecase=False, multiline=False):
368
"""Test if value matches regex pattern"""
369
370
def search(value, pattern, ignorecase=False, multiline=False):
371
"""Test if pattern found in value"""
372
373
def regex(value, pattern, ignorecase=False, multiline=False):
374
"""Test regex pattern against value"""
375
```
376
377
### Template Functions and Globals
378
379
Global functions and variables available in all template contexts providing access to Ansible-specific functionality.
380
381
```python { .api }
382
# Template functions
383
def range(start, stop=None, step=1):
384
"""Generate range of numbers"""
385
386
def lipsum(n=5, html=True, min=20, max=100):
387
"""Generate lorem ipsum text"""
388
389
def dict(items):
390
"""Create dictionary from items"""
391
392
def list(items):
393
"""Convert items to list"""
394
395
# Ansible-specific globals
396
hostvars: dict # All host variables
397
group_names: list # Groups current host belongs to
398
groups: dict # All groups and their hosts
399
inventory_hostname: str # Current hostname
400
ansible_hostname: str # Discovered hostname
401
ansible_facts: dict # Gathered facts
402
play_hosts: list # Hosts in current play
403
ansible_play_hosts: list # All play hosts
404
ansible_play_batch: list # Current batch of hosts
405
```
406
407
## Template Usage
408
409
### Variable Interpolation
410
411
```yaml
412
# Basic variable substitution
413
- name: "Install {{ package_name }}"
414
package:
415
name: "{{ package_name }}"
416
state: present
417
418
# Complex expressions
419
- name: "Setup {{ inventory_hostname }}"
420
template:
421
src: config.j2
422
dest: "/etc/{{ service_name }}/{{ inventory_hostname }}.conf"
423
vars:
424
config_file: "{{ service_name }}-{{ ansible_hostname }}.conf"
425
```
426
427
### Conditional Templates
428
429
```yaml
430
# Conditional content
431
- name: Configure service
432
template:
433
src: service.conf.j2
434
dest: /etc/service.conf
435
notify: restart service
436
437
# Template with conditionals
438
# service.conf.j2:
439
# {% if ssl_enabled %}
440
# ssl_certificate {{ ssl_cert_path }}
441
# ssl_certificate_key {{ ssl_key_path }}
442
# {% endif %}
443
#
444
# {% for port in service_ports %}
445
# listen {{ port }}
446
# {% endfor %}
447
```
448
449
### Filters in Templates
450
451
```yaml
452
# Using filters
453
- name: Display formatted data
454
debug:
455
msg: |
456
YAML: {{ my_dict | to_yaml }}
457
JSON: {{ my_dict | to_json }}
458
Base64: {{ secret_value | b64encode }}
459
460
# List processing
461
- name: Process user list
462
debug:
463
msg: "User {{ item }} has home {{ item | regex_replace('^(.+)$', '/home/\\1') }}"
464
loop: "{{ users | map('extract', hostvars, 'username') | list }}"
465
```
466
467
### Tests in Conditionals
468
469
```yaml
470
# Using tests
471
- name: Check conditions
472
debug:
473
msg: "Processing {{ item }}"
474
loop: "{{ server_list }}"
475
when:
476
- item is string
477
- item is match('^web.*')
478
- inventory_hostname is version('2.0', '>=')
479
480
# Network tests
481
- name: Configure network
482
template:
483
src: network.j2
484
dest: /etc/network/interfaces
485
when: ansible_default_ipv4.address is ip
486
```
487
488
### Complex Template Examples
489
490
```jinja2
491
{# config.j2 template #}
492
# Generated by Ansible on {{ ansible_date_time.date }}
493
# Host: {{ inventory_hostname }}
494
495
{% set server_config = {
496
'web': {'port': 80, 'workers': 4},
497
'api': {'port': 8080, 'workers': 2},
498
'db': {'port': 5432, 'workers': 1}
499
} %}
500
501
{% for service_type in groups %}
502
{% if service_type in server_config %}
503
[{{ service_type }}]
504
{% for host in groups[service_type] %}
505
{{ host }} = {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ server_config[service_type]['port'] }}
506
{% endfor %}
507
workers = {{ server_config[service_type]['workers'] }}
508
509
{% endif %}
510
{% endfor %}
511
512
# SSL Configuration
513
{% if ssl_enabled | default(false) %}
514
ssl_cert = {{ ssl_cert_path | default('/etc/ssl/certs/server.crt') }}
515
ssl_key = {{ ssl_key_path | default('/etc/ssl/private/server.key') }}
516
{% endif %}
517
518
# Environment-specific settings
519
{% if environment == 'production' %}
520
debug = false
521
log_level = warn
522
{% else %}
523
debug = true
524
log_level = debug
525
{% endif %}
526
```
527
528
## Template Security
529
530
### Trusted Templates
531
532
```python
533
# Template trust checking
534
from ansible.template import Templar
535
from ansible.errors import TemplateTrustCheckFailedError
536
537
templar = Templar(loader=loader, variables=variables)
538
539
try:
540
# This will check template trust
541
result = templar.template(untrusted_template)
542
except TemplateTrustCheckFailedError:
543
print("Template from untrusted source rejected")
544
```
545
546
### Safe Template Processing
547
548
```python
549
# Safe templating with error handling
550
from ansible.template import Templar
551
from ansible.errors import AnsibleTemplateError
552
553
templar = Templar(loader=loader, variables=variables)
554
555
try:
556
result = templar.template("{{ undefined_var }}", fail_on_undefined=True)
557
except AnsibleTemplateError as e:
558
print(f"Template error: {e}")
559
```
560
561
## Usage Examples
562
563
### Basic Templating
564
565
```python
566
from ansible.template import Templar
567
from ansible.parsing.dataloader import DataLoader
568
569
# Initialize components
570
loader = DataLoader()
571
variables = {
572
'service_name': 'nginx',
573
'port': 80,
574
'ssl_enabled': True
575
}
576
577
# Create templater
578
templar = Templar(loader=loader, variables=variables)
579
580
# Template strings
581
result = templar.template("Service {{ service_name }} on port {{ port }}")
582
print(result) # "Service nginx on port 80"
583
584
# Template with conditionals
585
template = "{% if ssl_enabled %}HTTPS{% else %}HTTP{% endif %} enabled"
586
result = templar.template(template)
587
print(result) # "HTTPS enabled"
588
```
589
590
### Advanced Templating
591
592
```python
593
# Template complex data structures
594
data = {
595
'config': {
596
'servers': [
597
{'name': 'web1', 'ip': '192.168.1.10'},
598
{'name': 'web2', 'ip': '192.168.1.11'}
599
]
600
},
601
'template_var': '{{ config.servers | map(attribute="name") | join(",") }}'
602
}
603
604
result = templar.template(data)
605
print(result['template_var']) # "web1,web2"
606
607
# Check if data needs templating
608
needs_template = templar.is_template("{{ service_name }}")
609
print(needs_template) # True
610
611
no_template = templar.is_template("static_string")
612
print(no_template) # False
613
```