Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool
Ansible Playbook JSON Configuration Strategies
Ansible playbooks are primarily written in YAML, a human-readable data serialization format. However, in modern automation workflows, you often need to interact with data in JSON format. This could be fetching data from APIs, processing output from commands, defining complex nested variables, or configuring applications that expect JSON input.
Integrating JSON effectively into your Ansible playbooks requires understanding how Ansible handles data transformation and manipulation. This article explores common strategies for working with JSON within your playbooks.
Handling JSON Data with Filters
Ansible provides built-in filters powered by Jinja2 to easily convert between YAML/Python objects and JSON strings. The two primary filters for this are to_json and from_json.
The to_json Filter
The to_json filter converts an Ansible variable (which is essentially a Python data structure like a dictionary or list) into a JSON formatted string. This is useful when you need to pass data to a command-line tool, an API call, or write it to a file in JSON format.
You can optionally pass arguments like indent to make the output human-readable.
Example: Using to_json
---
- name: Demonstrate to_json filter
hosts: localhost
gather_facts: false
vars:
my_data:
name: "Ansible User"
roles: ["developer", "sysadmin"]
settings:
verbose: true
level: 5
tasks:
- name: Output data as a JSON string
debug:
msg: "{{ my_data | to_json }}"
- name: Output data as a pretty-printed JSON string
debug:
msg: "{{ my_data | to_json(indent=2) }}"
- name: Create a JSON file from the variable
copy:
content: "{{ my_data | to_json(indent=2) }}"
dest: /tmp/my_config.json
The first debug task will output a compact JSON string. The second will output a formatted, indented string, which is often easier to read. The copy task demonstrates how to write this JSON string to a file.
The from_json Filter
The from_json filter (or its alias from_yaml, as YAML is a superset of JSON) takes a JSON formatted string and converts it into an Ansible variable (a Python dictionary or list). This is essential when you read JSON from a file, receive it from an API response, or capture it from command output.
Example: Using from_json
---
- name: Demonstrate from_json filter
hosts: localhost
gather_facts: false
tasks:
- name: Simulate receiving JSON data from a command/API
set_fact:
json_output_string: '{"status": "success", "data": {"id": 123, "items": ["apple", "banana"]}}'
- name: Convert JSON string to Ansible variable
set_fact:
parsed_data: "{{ json_output_string | from_json }}"
- name: Access elements from the parsed JSON data
debug:
msg: "Status: {{ parsed_data.status }}, First item: {{ parsed_data.data.items[0] }}"
After applying from_json, you can navigate the resulting Ansible variable using standard dot or bracket notation, just like any other variable in your playbook.
Storing JSON Data in Variables
While you can store JSON as a string and use from_json, Ansible (being YAML-based) is perfectly capable of representing nested data structures directly.
Often, a JSON structure can be directly translated into equivalent YAML structure within a playbook's vars section or a separate variable file. Ansible will handle this nested YAML as the corresponding Python dictionary or list, which is compatible with JSON structure.
Example: JSON-like structure in Ansible Vars
---
- name: JSON structure stored directly in vars
hosts: localhost
gather_facts: false
vars:
user_config:
user:
name: "Charlie"
id: 456
preferences:
theme: "dark"
notifications:
email: true
sms: false
permissions:
- role: "editor"
level: "high"
- role: "viewer"
level: "low"
tasks:
- name: Access nested data
debug:
msg: "User name: {{ user_config.user.name }}, Email notifications: {{ user_config.user.preferences.notifications.email }}"
- name: Convert this structure to JSON string
debug:
msg: "{{ user_config | to_json(indent=2) }}"
This approach is generally cleaner and more readable for complex static configuration data than embedding large JSON strings.
Reading JSON from Files
You can store complex JSON configuration data in separate .json files and read them into your playbook using the include_vars or vars_files keywords. Ansible will automatically parse the JSON content into a variable.
Example: Reading JSON from a file (config.json)
Contents of config.json:
{
"app": {
"name": "WebApp",
"version": "1.0.0",
"enabled_features": ["auth", "logging"]
},
"database": {
"host": "db.example.com",
"port": 5432
}
}Playbook snippet:
---
- name: Read config from JSON file
hosts: localhost
gather_facts: false
vars_files:
- config.json # Ansible automatically parses this as JSON/YAML
tasks:
- name: Access data read from the JSON file
debug:
msg: "App name: {{ app.name }}, DB Host: {{ database.host }}"
- name: Convert the loaded data back to JSON (optional, for verification)
debug:
msg: "{{ vars | to_json(indent=2) }}" # 'vars' includes variables from vars_files
When using vars_files or include_vars, Ansible is smart enough to detect the file format (YAML or JSON) and load it accordingly, making this a straightforward way to manage external JSON configuration.
Querying JSON Data with json_query
For complex JSON data structures, navigating manually with dot/bracket notation can become cumbersome, especially when dealing with lists or needing to filter data. The json_query filter, based on JMESPath, provides a powerful way to query JSON structures.
Example: Using json_query
---
- name: Demonstrate json_query filter
hosts: localhost
gather_facts: false
vars:
complex_data:
users:
- id: 1
name: "Alice"
active: true
roles: ["admin", "user"]
- id: 2
name: "Bob"
active: false
roles: ["user"]
- id: 3
name: "Charlie"
active: true
roles: ["editor", "user"]
settings:
default_role: "user"
tasks:
- name: Get names of all users
debug:
msg: "{{ complex_data | json_query('users[*].name') }}" # [ "Alice", "Bob", "Charlie" ]
- name: Get users who are active
debug:
msg: "{{ complex_data | json_query('users[?active == `true`]') | to_json(indent=2) }}"
# Outputs list of user objects where active is true
- name: Get names of users who have the 'admin' role
debug:
msg: "{{ complex_data | json_query('users[?contains(roles, `'admin'`)].name') }}" # [ "Alice" ]
- name: Get default setting
debug:
msg: "{{ complex_data | json_query('settings.default_role') }}" # "user"
JMESPath syntax takes some learning, but it's incredibly powerful for extracting exactly the data you need from complex JSON structures, making your playbooks cleaner and more robust.
Best Practices
- •Use
from_json/from_yamlfor parsing: Always convert incoming JSON strings to Ansible variables using these filters before attempting to access their elements. - •Prefer YAML structure for static data: If your JSON data is part of your playbook's static configuration, define it directly using YAML's native nested structure instead of embedding JSON strings.
- •Store large/complex JSON in separate files: Use
vars_filesorinclude_varsto keep your playbooks clean when dealing with substantial JSON configurations. - •Leverage
json_queryfor complex data extraction: Invest time in learning JMESPath if you frequently work with nested or list-heavy JSON outputs. - •Handle potential errors: JSON operations can fail if the input string is invalid or the query path doesn't exist. Use conditional checks or error handling (e.g.,
failed_when,ignore_errors, usingdefault(omit)or| d()/| d([])) where appropriate.
Common Pitfalls
- •Not parsing JSON strings: Trying to access elements of a JSON string directly (e.g.,
"{...}".status) will fail. Always use| from_jsonfirst. - •YAML vs. JSON syntax: While YAML can represent JSON, be mindful of indentation and special characters in YAML strings if manually embedding JSON strings. Using the
| to_jsonfilter is safer for generating JSON strings. - •Complex queries without
json_query: Deeply nested access likevariable['key1']['key2'][0]['subkey']becomes hard to read and maintain quickly. Usejson_queryfor anything beyond basic access.
Conclusion
Effectively managing JSON data is a crucial skill for modern Ansible automation. By utilizing Ansible's built-in filters like to_json, from_json, and json_query, and by structuring your variable data appropriately, you can seamlessly integrate JSON into your playbooks, making them capable of interacting with a wider range of systems and data sources.
Understanding these strategies allows you to build more flexible, powerful, and maintainable automation workflows that can handle complex configuration scenarios.
Need help with your JSON?
Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool