AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

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 GuideAAP 2.6 Credential Management: Vaults, External Secrets, and Machine CredentialsAAP 2.6 Architecture and Components: Complete GuideAAP 2.6 RBAC and Gateway APIAAP 2.6 Configuration as Code with ansible.platform

Category: troubleshooting

Browse all Ansible tutorials · AnsiblePilot Home