AAP 2.6 Job Templates and Inventories: Complete Configuration Guide
By Luca Berton · Published 2024-01-01 · Category: troubleshooting
Configure job templates and inventories in AAP 2.6: static and dynamic inventories, smart inventories, job template surveys, scheduling, notification.
Job Templates Overview
Job Templates in AAP 2.6 are the fundamental unit of automation execution. They combine: • A playbook (from a Project) • An inventory (target hosts) • Credentials (authentication) • An Execution Environment (runtime container) • Optional surveys (runtime input) • Optional schedules (recurring execution) • Optional notifications (alerts on success/failure)
See also: AAP 2.6 Workflow Templates: Advanced Multi-Step Automation Guide
Creating Job Templates
Via the ansible.platform Collection
- name: Create a job template
ansible.platform.job_template:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Linux Patching - Production"
organization: "Operations"
project: "infrastructure-playbooks"
playbook: "patch-linux.yml"
inventory: "Production Linux Servers"
credentials:
- "Production SSH Key"
- "Ansible Vault - Production"
execution_environment: "ee-supported-rhel9"
job_type: "run"
verbosity: 0
forks: 20
limit: ""
extra_vars:
patch_reboot: true
patch_exclude: "kernel*"
ask_limit_on_launch: true
ask_variables_on_launch: false
state: present
Key Job Template Settings
| Setting | Description | Default |
|---------|-------------|---------|
| job_type | run (execute) or check (dry run) | run |
| verbosity | 0 (normal) to 5 (connection debug) | 0 |
| forks | Parallel host execution | 5 |
| limit | Restrict to subset of inventory | (all hosts) |
| timeout | Job timeout in seconds | 0 (no timeout) |
| diff_mode | Show file changes | false |
| become_enabled | Enable privilege escalation | false |
Ask-on-Launch Options
Enable users to override settings at launch time:
- name: Flexible job template
ansible.platform.job_template:
name: "Deploy Application"
ask_inventory_on_launch: true # Choose inventory at launch
ask_credential_on_launch: true # Choose credentials at launch
ask_limit_on_launch: true # Specify host limit at launch
ask_variables_on_launch: true # Provide extra vars at launch
ask_job_type_on_launch: true # Choose run/check at launch
ask_verbosity_on_launch: true # Set verbosity at launch
ask_execution_environment_on_launch: true
state: present
Inventory Types
Static Inventory
Define hosts and groups manually:
- name: Create static inventory
ansible.platform.inventory:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Production Linux Servers"
organization: "Operations"
description: "All production Linux servers"
state: present
- name: Add host group
ansible.platform.group:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "webservers"
inventory: "Production Linux Servers"
state: present
- name: Add host to group
ansible.platform.host:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "web01.example.com"
inventory: "Production Linux Servers"
enabled: true
variables:
ansible_host: 192.168.1.10
http_port: 8080
state: present
Dynamic Inventory (Cloud Sources)
Auto-discover hosts from cloud providers:
# AWS EC2 dynamic inventory
- name: Add AWS inventory source
ansible.platform.inventory_source:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "AWS Production"
inventory: "Cloud Inventory"
source: "amazon.aws.aws_ec2"
credential: "AWS Production"
source_vars:
regions:
- us-east-1
- us-west-2
filters:
tag:Environment: production
instance-state-name: running
keyed_groups:
- key: tags.Role
prefix: role
- key: placement.availability_zone
prefix: az
compose:
ansible_host: private_ip_address
update_on_launch: true
overwrite: true
state: present
# Azure dynamic inventory
- name: Add Azure inventory source
ansible.platform.inventory_source:
name: "Azure Production"
inventory: "Cloud Inventory"
source: "azure.azcollection.azure_rm"
credential: "Azure Production"
source_vars:
include_vm_resource_groups:
- "production-rg"
keyed_groups:
- key: tags.role | default('untagged')
prefix: role
conditional_groups:
linux: "'linux' in image.offer | lower"
windows: "'windows' in image.offer | lower"
update_on_launch: true
state: present
# VMware vCenter dynamic inventory
- name: Add vCenter inventory source
ansible.platform.inventory_source:
name: "vCenter Production"
inventory: "VMware Inventory"
source: "community.vmware.vmware_vm_inventory"
credential: "vCenter Production"
source_vars:
hostnames:
- config.name
properties:
- name
- guest.ipAddress
- config.cpuCount
- config.memoryMB
- runtime.powerState
filters:
- runtime.powerState == "poweredOn"
keyed_groups:
- key: config.guestId
prefix: os
update_on_launch: true
state: present
Smart Inventories
Smart inventories dynamically group hosts from other inventories using filters:
- name: Create smart inventory for Linux servers needing patches
ansible.platform.inventory:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Linux Servers - Patch Pending"
organization: "Operations"
kind: "smart"
host_filter: "ansible_facts__os_family=\"RedHat\" and ansible_facts__distribution_major_version__gt=\"7\""
state: present
Smart inventory use cases:
| Filter | Use Case |
|--------|----------|
| groups__name=webservers | All hosts in webservers group (any inventory) |
| ansible_facts__os_family=Debian | All Debian-family hosts |
| name__startswith=prod- | Hosts with names starting with "prod-" |
| ansible_facts__ansible_kernel__contains=el9 | RHEL 9 hosts |
Constructed Inventories
Combine and transform multiple inventory sources:
- name: Create constructed inventory
ansible.platform.inventory:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Multi-Cloud Production"
organization: "Operations"
kind: "constructed"
source_vars:
plugin: constructed
strict: false
groups:
all_webservers: "'webserver' in group_names or 'web' in group_names"
all_databases: "'database' in group_names or 'db' in group_names"
patch_tuesday: "ansible_facts.os_family == 'Windows'"
compose:
environment: "'production'"
state: present
See also: Ansible Automation Platform 2.6 Architecture and Components: Complete Guide
Surveys
Surveys create forms that collect input at job launch:
- name: Configure deployment survey
ansible.platform.job_template:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Deploy Application"
survey_enabled: true
survey_spec:
name: "Deployment Parameters"
description: "Configure your deployment"
spec:
- question_name: "Application Version"
question_description: "Semantic version to deploy"
variable: "app_version"
type: "text"
required: true
default: "latest"
min: 1
max: 20
- question_name: "Environment"
variable: "deploy_env"
type: "multiplechoice"
choices:
- staging
- production
required: true
default: "staging"
- question_name: "Enable Maintenance Mode"
variable: "maintenance_mode"
type: "multiplechoice"
choices:
- "yes"
- "no"
default: "yes"
- question_name: "Deployment Notes"
variable: "deploy_notes"
type: "textarea"
required: false
min: 0
max: 500
- question_name: "Max Parallel Hosts"
variable: "serial_count"
type: "integer"
required: true
default: 5
min: 1
max: 50
- question_name: "Admin Password"
variable: "admin_password"
type: "password"
required: true
state: present
Survey question types:
| Type | Widget | Notes |
|------|--------|-------|
| text | Text input | min/max character length |
| textarea | Multi-line text | min/max character length |
| password | Password input | Encrypted, not shown in output |
| integer | Number input | min/max value |
| float | Decimal input | min/max value |
| multiplechoice | Dropdown select | Single selection |
| multiselect | Checkbox list | Multiple selections |
Scheduling Jobs
Recurring Schedules
# Daily at 2 AM UTC
- name: Schedule nightly patching
ansible.platform.schedule:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Nightly Patching - Staging"
unified_job_template: "Linux Patching - Staging"
rrule: "DTSTART:20260101T020000Z RRULE:FREQ=DAILY;INTERVAL=1"
enabled: true
state: present
# Every Tuesday at 10 PM (Patch Tuesday)
- name: Schedule Patch Tuesday
ansible.platform.schedule:
name: "Patch Tuesday - Windows"
unified_job_template: "Windows Patching"
rrule: "DTSTART:20260101T220000Z RRULE:FREQ=WEEKLY;BYDAY=TU"
enabled: true
state: present
# First Monday of every month at 6 AM
- name: Schedule monthly compliance scan
ansible.platform.schedule:
name: "Monthly Compliance Scan"
unified_job_template: "CIS Compliance Audit"
rrule: "DTSTART:20260101T060000Z RRULE:FREQ=MONTHLY;BYDAY=1MO"
extra_data:
report_email: "security@example.com"
state: present
# Every 4 hours
- name: Schedule inventory refresh
ansible.platform.schedule:
name: "Cloud Inventory Refresh"
unified_job_template: "Refresh All Inventories"
rrule: "DTSTART:20260101T000000Z RRULE:FREQ=HOURLY;INTERVAL=4"
state: present
See also: AAP 2.6 Backup, Restore, and Disaster Recovery Guide
Notification Templates
Get alerted when jobs succeed, fail, or start:
# Slack notification
- name: Create Slack notification
ansible.platform.notification_template:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
name: "Slack - Operations Channel"
notification_type: "slack"
notification_configuration:
token: "{{ slack_token }}"
channels:
- "#ops-automation"
state: present
# Email notification
- name: Create email notification
ansible.platform.notification_template:
name: "Email - Operations Team"
notification_type: "email"
notification_configuration:
host: "smtp.example.com"
port: 587
username: "automation@example.com"
password: "{{ smtp_password }}"
use_tls: true
sender: "automation@example.com"
recipients:
- "ops-team@example.com"
state: present
# Webhook notification (ServiceNow, PagerDuty, etc.)
- name: Create webhook notification
ansible.platform.notification_template:
name: "ServiceNow - Incident"
notification_type: "webhook"
notification_configuration:
url: "https://instance.service-now.com/api/now/table/incident"
username: "automation"
password: "{{ snow_password }}"
http_method: "POST"
headers:
Content-Type: "application/json"
state: present
# Attach notification to job template
- name: Attach notifications to patching template
ansible.platform.job_template:
name: "Linux Patching - Production"
notification_templates_started:
- "Slack - Operations Channel"
notification_templates_success:
- "Email - Operations Team"
notification_templates_error:
- "Slack - Operations Channel"
- "Email - Operations Team"
- "ServiceNow - Incident"
state: present
Launching Jobs
Via API
# Launch with defaults
curl -s -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://gateway.example.org/api/controller/v2/job_templates/42/launch/"
# Launch with extra variables
curl -s -k -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"extra_vars": {"app_version": "2.4.1"}, "limit": "webservers"}' \
"https://gateway.example.org/api/controller/v2/job_templates/42/launch/"
Via Playbook
- name: Launch a job and wait for completion
ansible.platform.job_launch:
controller_host: "{{ gateway_url }}"
controller_username: "{{ controller_user }}"
controller_password: "{{ controller_pass }}"
job_template: "Linux Patching - Production"
extra_vars:
patch_reboot: false
limit: "web01.example.com"
wait: true
timeout: 600
register: job_result
- name: Show job status
ansible.builtin.debug:
msg: "Job {{ job_result.id }} finished with status: {{ job_result.status }}"
FAQ
What is the difference between a job template and a workflow template?
A job template runs a single playbook against an inventory. A workflow template chains multiple job templates (and other actions) into a pipeline with branching logic, approval gates, and convergence. Use job templates for individual tasks; use workflows for multi-step processes.
Can I limit dynamic inventory to specific hosts?
Yes. Use the limit field on the job template or at launch time. You can also use host_filter in smart inventories to create permanent subsets. Dynamic inventory source_vars support filters to restrict which hosts are discovered.
How do I handle different variables per environment?
Use inventory-level variables. Create separate inventories for each environment (staging, production) with different variable values. The same job template can run against different inventories using ask_inventory_on_launch.
Can I revert a failed job?
AAP does not have built-in rollback. Design your playbooks to be idempotent so re-running is safe. For complex rollback needs, use workflow templates with failure paths that trigger rollback job templates.
What happens when a scheduled job overlaps with a running instance?
By default, AAP will queue the new job. You can configure instance groups and capacity to control concurrent execution. There is no built-in "skip if already running" — implement this logic in your playbook or use workflow convergence.
Conclusion
Job templates and inventories are the workhorses of AAP 2.6. Mastering dynamic inventories, surveys, scheduling, and notifications turns AAP from a manual job runner into a self-service automation platform where teams can safely execute approved automation with proper guardrails.
Related Articles
• AAP 2.6 Workflow Templates: Advanced Multi-Step Automation Guide • AAP 2.6 Credential Management: Vaults, External Secrets, and Machine Credentials • AAP 2.6 Architecture and Components: Complete Guide • AAP 2.6 RBAC and Gateway API • AAP 2.6 Configuration as Code with ansible.platformCategory: troubleshooting