JSON Usage in Ansible
Last updated: June 29, 2025
My JSON Journey with Ansible
When I first started working with Ansible, I was primarily focused on simple tasks like package installation and service management. But as my automation needs grew more complex, I found myself constantly handling data from APIs, external systems, and configuration files—most of it in JSON format. Learning how to effectively work with JSON in Ansible has been a game-changer for my automation workflows, allowing me to handle complex data transformations that would otherwise require custom scripts. In this post, I'll share my experiences and practical approaches for working with JSON in Ansible, with examples for both Linux and Windows environments.
Understanding JSON in Ansible
JSON (JavaScript Object Notation) has become the lingua franca of data exchange in modern IT systems. Its lightweight, human-readable structure makes it perfect for storing configuration data, API responses, and structured information. In Ansible, JSON is a fundamental data format that powers many internal operations.
JSON Data Types
Before diving into the practical applications, let's quickly review the basic JSON data types you'll encounter in Ansible:
Strings: Text enclosed in quotes (
"example"
)Numbers: Integer or floating-point values (
42
,3.14
)Booleans: True/false values (
true
,false
)Null: Represents the absence of a value (
null
)Arrays: Ordered collections of values (
["item1", "item2"]
)Objects: Collections of key-value pairs (
{"key": "value"}
)
The most powerful and commonly used JSON structures in Ansible are objects and arrays. JSON objects in Ansible are similar to Python dictionaries, while JSON arrays are similar to Python lists.
JSON Objects in Ansible
In my experience, JSON objects (or dictionaries in Python terms) are the most common JSON structure you'll work with in Ansible. Here's a typical example of a JSON object representation in Ansible:
{
"server": "web01",
"location": "us-east",
"environment": "production",
"services": {
"http": {
"port": 80,
"enabled": true
},
"https": {
"port": 443,
"enabled": true
}
}
}
Accessing JSON Object Values
I've found two primary ways to access values in JSON objects:
Dot Notation: Simple and readable, but doesn't work with keys containing spaces or special characters
# Accessing nested values with dot notation - debug: msg: "The HTTP port is {{ server_config.services.http.port }}"
Bracket Notation: More verbose but works with all key types
# Accessing nested values with bracket notation - debug: msg: "The HTTPS port is {{ server_config['services']['https']['port'] }}"
JSON Arrays in Ansible
JSON arrays are ordered collections of values that work similarly to Python lists. Here's an example of a JSON array:
[
"web01",
"web02",
"web03"
]
More commonly, you'll see arrays of objects, which are particularly useful for representing collections of similar items:
[
{
"server": "web01",
"ip": "192.168.1.101"
},
{
"server": "web02",
"ip": "192.168.1.102"
}
]
Accessing JSON Array Values
You can access array elements by their index (starting from 0):
- debug:
msg: "The first server is {{ servers[0].server }} with IP {{ servers[0].ip }}"
Working with JSON in Variables
One of the first advanced techniques I learned was storing complex data structures in Ansible variables. This is particularly useful for configuration data:
---
# group_vars/webservers.yml
webserver_config:
listen_port: 80
document_root: "/var/www/html"
virtual_hosts:
- name: "example.com"
root: "/var/www/example"
- name: "blog.example.com"
root: "/var/www/blog"
modules:
- "mod_ssl"
- "mod_rewrite"
This YAML structure is internally converted to a Python dictionary (which is equivalent to a JSON object) and can be accessed just like JSON data.
Converting Between JSON and Other Formats
Ansible provides several filters for converting between JSON and other formats. These have been incredibly useful in my automation work:
Converting Strings to JSON (Parsing JSON)
When you receive JSON data as a string (for example, from an API response), you need to parse it:
- name: Get data from API
uri:
url: https://api.example.com/data
method: GET
register: api_response
- name: Parse JSON response
set_fact:
parsed_data: "{{ api_response.content | from_json }}"
Converting Variables to JSON Strings
When you need to send data to an API or write it to a file:
- name: Prepare API payload
set_fact:
api_payload: "{{ {'name': 'example', 'value': 42} | to_json }}"
- name: Send data to API
uri:
url: https://api.example.com/update
method: POST
body: "{{ api_payload }}"
body_format: json
Real-world Example: Configuring Application Settings
Here's a practical example from my work. I needed to update a configuration file on both Linux and Windows servers based on environment-specific settings:
---
- name: Update application configuration
hosts: all
vars:
base_config:
app_name: "MyApp"
version: "1.2.3"
logging:
level: "info"
path: "{{ 'C:\\Logs' if ansible_os_family == 'Windows' else '/var/log' }}"
database:
type: "postgres"
host: "db.example.com"
tasks:
- name: Merge with environment-specific settings
set_fact:
final_config: "{{ base_config | combine(env_specific_config, recursive=True) }}"
vars:
env_specific_config: "{{ lookup('file', 'configs/{{ environment }}.json') | from_json }}"
# Windows-specific configuration
- name: Update Windows config file
win_copy:
content: "{{ final_config | to_nice_json }}"
dest: C:\Program Files\MyApp\config.json
when: ansible_os_family == "Windows"
# Linux-specific configuration
- name: Update Linux config file
copy:
content: "{{ final_config | to_nice_json }}"
dest: /etc/myapp/config.json
when: ansible_os_family != "Windows"
In this example, I combine a base configuration with environment-specific overrides loaded from a JSON file.
Advanced JSON Manipulation with json_query
As I started working with more complex data structures, I discovered the power of json_query
. This filter implements JMESPath, a query language for JSON. It's invaluable for extracting specific data from complex structures.
Installing JMESPath
Before using json_query
, you need to install the JMESPath Python module on your Ansible controller:
pip install jmespath
Basic Filtering
Let's say you have a complex API response and want to extract specific information:
- name: Filter specific data from complex JSON
debug:
msg: "{{ complex_data | json_query('servers[*].name') }}"
This would extract all server names from an array of server objects.
Real-world Example: Processing API Results
Here's a practical example where I use json_query
to transform data from an API into a more usable format:
---
- name: Process VM information
hosts: localhost
gather_facts: false
tasks:
- name: Get VM info from API
uri:
url: https://vcenter.example.com/api/vms
method: GET
register: vm_api_result
- name: Parse API response
set_fact:
vm_data: "{{ vm_api_result.content | from_json }}"
- name: Transform data for reporting
set_fact:
vm_report: "{{ vm_data | json_query('[].{name: item, status: instance.guest.guestState}') }}"
- name: Show transformed data
debug:
var: vm_report
This example extracts just the VM names and their guest states from a more complex nested structure.
Windows Example: Processing Windows Features
For Windows automation, here's an example of using JSON manipulation to process Windows feature information:
---
- name: Process Windows features
hosts: windows_servers
tasks:
- name: Get Windows feature info
win_feature_info:
register: feature_info
- name: Get list of installed features
set_fact:
installed_features: "{{ feature_info.features | json_query('[?installed==`true`].{name: name, display_name: display_name}') }}"
- name: Show installed features
debug:
var: installed_features
Advanced JSON Transformations
As your automation becomes more complex, you'll often need to transform JSON data in more sophisticated ways. Here's a sequence diagram showing how this typically works in an Ansible playbook:
This diagram illustrates the typical flow when working with JSON data in Ansible:
Fetch data from an external source
Parse the JSON string into Python objects
Query and transform the data as needed
Convert back to JSON if required (for APIs or file storage)
Apply the transformed data to target systems
Practical Tips from Experience
After years of working with JSON in Ansible, here are some practical tips I've learned:
1. Use to_nice_json
for Readable Output
to_nice_json
for Readable OutputWhen saving JSON to files or displaying it for debugging, use the to_nice_json
filter instead of to_json
to get nicely formatted, human-readable output:
- name: Write readable JSON to file
copy:
content: "{{ complex_data | to_nice_json }}"
dest: /tmp/debug.json
2. Validate JSON Before Processing
When working with external sources, validate that the JSON is well-formed:
- name: Attempt to parse JSON
set_fact:
parsed_data: "{{ input_string | from_json }}"
ignore_errors: yes
register: parse_result
- name: Handle invalid JSON
debug:
msg: "The input contains invalid JSON"
when: parse_result is failed
3. Use the default
Filter for Missing Values
default
Filter for Missing ValuesWhen accessing potentially missing keys, use the default
filter to provide fallback values:
- name: Safely access value with default
debug:
msg: "The HTTP port is {{ config.services.http.port | default(80) }}"
4. Combine Filters for Complex Transformations
Chain filters together for more complex transformations:
- name: Complex transformation
debug:
msg: "{{ api_data | json_query('results[*].name') | map('regex_replace', '^srv-', '') | list }}"
Windows-specific JSON Handling
When working with Windows systems, I've found these JSON techniques particularly useful:
Working with PowerShell Output
PowerShell can output JSON directly, which makes it easy to integrate with Ansible:
- name: Run PowerShell command and get JSON output
win_shell: Get-Process | Select-Object -First 5 | ConvertTo-Json
register: process_output
- name: Parse PowerShell JSON output
set_fact:
processes: "{{ process_output.stdout | from_json }}"
- name: Show process names
debug:
msg: "{{ processes | json_query('[*].Name') }}"
Creating Windows Configuration Files
Many Windows applications use JSON for their configuration files. Here's how I generate them:
- name: Create Windows application config
win_copy:
content: |
{{ {
'settings': {
'installDir': 'C:\\Program Files\\MyApp',
'dataDir': 'C:\\ProgramData\\MyApp',
'autoStart': true,
'logging': {
'level': 'info',
'file': 'C:\\Logs\\MyApp\\app.log'
}
}
} | to_nice_json }}
dest: C:\ProgramData\MyApp\config.json
Conclusion
JSON has become integral to my Ansible workflows, enabling complex data handling that would otherwise require custom scripting. The ability to parse, query, transform, and generate JSON has been particularly valuable when working with modern infrastructure that relies heavily on APIs and configuration files.
Whether you're working with Linux or Windows systems, mastering JSON usage in Ansible will significantly expand what you can achieve with your automation. From simple variable manipulation to complex data transformations, the techniques I've shared here should help you handle virtually any JSON-related task in your automation journey.
As with many aspects of Ansible, I recommend starting simple and gradually incorporating more advanced techniques as your comfort level grows. Before you know it, you'll be transforming complex JSON structures with ease!
What JSON-related challenges have you encountered in your Ansible automation? I'd love to hear your experiences and solutions in the comments below!
Last updated