At Red Hat Tech Day Netherlands 2026 in Bunnik, Fred van Zwieten and Ismail Dhaoui demoed the new Automation Portal in AAP 2.7 — a self-service template gallery where every template has a one-click "Start" button. One of the branch-office templates shown live was Branch - After Hours Access Report, which generates a report of access events that occurred outside normal operating hours. Below is a runnable playbook that implements that use case.
Playbook
---
- name: Branch - After Hours Access Report
hosts: branch_access_controllers
gather_facts: false
vars:
business_hours_start: "07:00"
business_hours_end: "19:00"
report_dir: "/tmp/access-reports"
report_file: "{{ report_dir }}/{{ inventory_hostname }}-after-hours-{{ ansible_date_time.date | default(lookup('pipe', 'date +%F')) }}.json"
tasks:
- name: Ensure local report directory exists
ansible.builtin.file:
path: "{{ report_dir }}"
state: directory
mode: "0750"
delegate_to: localhost
run_once: true
- name: Pull access log entries from the branch controller
ansible.builtin.command:
cmd: >-
badge-access-cli export
--since "{{ ansible_date_time.date | default(lookup('pipe', 'date +%F')) }}T00:00:00"
--format json
register: access_log_raw
changed_when: false
- name: Parse access log JSON
ansible.builtin.set_fact:
access_events: "{{ access_log_raw.stdout | from_json }}"
- name: Filter events outside business hours
ansible.builtin.set_fact:
after_hours_events: >-
{{ access_events | selectattr('event_time_local', 'lt', business_hours_start)
| list + access_events | selectattr('event_time_local', 'gt', business_hours_end) | list }}
- name: Write after-hours access report
ansible.builtin.copy:
content: "{{ after_hours_events | to_nice_json }}"
dest: "{{ report_file }}"
mode: "0640"
delegate_to: localhost
- name: Report summary
ansible.builtin.debug:
msg: >-
{{ after_hours_events | length }} after-hours access event(s)
found for {{ inventory_hostname }}, written to {{ report_file }}.
What it does
The play targets a branch_access_controllers group — one host per branch badge/door controller, or a jump host that can query them. It exports the day's access log as JSON via a badge-access-cli call (a stand-in for whatever access-control vendor CLI or API a given branch runs), then uses selectattr to keep only events whose local timestamp falls before business_hours_start or after business_hours_end. The filtered list is written to a per-host JSON report and a one-line summary is printed for the console or job log.
This is exactly the kind of job the Automation Portal's self-service gallery is built for: a branch manager or security analyst clicks Start on "Branch - After Hours Access Report," the underlying job template runs against the correct inventory, and the operator never has to know which repo the playbook lives in, which credentials it uses, or how the time-window logic works. The portal handles the surveys and credential injection; the playbook stays a normal, testable piece of Ansible content in source control.
Notes / Gotchas
- Time zones matter.
event_time_localassumes the controller already reports local branch time. If your devices log in UTC, add a step to convert withansible.builtin.set_factand a Jinjastrftimefilter (or normalize upstream) before filtering. badge-access-cliis illustrative. Swap in your actual access-control integration — many vendors ship a REST API instead, in which case replace thecommandtask withansible.builtin.uri.- Business hours as survey inputs. In a job template, expose
business_hours_start/business_hours_endas survey variables so the same playbook covers branches with different opening hours without editing YAML. - Delegate the write, not the read. The report file is written on
localhost(the control node / execution environment) so a single consolidated report directory is easy to ship to object storage or a SIEM afterward. - Idempotency: this playbook is reporting-only — it never mutates state on the target, so
changed_when: falseon the log pull keeps job output honest.
Takeaway
A short, focused playbook like this is all the "Branch - After Hours Access Report" template needs behind its one-click Start button — the value AAP 2.7's Automation Portal adds is making that logic discoverable and runnable by non-Ansible-experts, not making the playbook itself more complex.
Related Articles
- Ansible hashicorp.vault Module Example: Dynamic Secrets in a Playbook
- Ansible ansible.platform Collection Example: AAP RBAC as Code
- Ansible Automation Platform 2.7 Changelog: A Field Guide
- AAP 2.7 PostgreSQL 17 and Django 5.2 LTS: What Changed Under the Hood