"Show me everyone who could access the Payments project on March 3rd." It sounds like a lookup. In Jira Cloud it is closer to archaeology, and if the date is more than about six months back it is usually impossible. This post explains why, gives a snapshot method you can script, and describes the packaged version.
Why Jira Cloud cannot answer this natively
- Project settings are a live view. The roles page shows who is in each role right now. There is no "as of" selector and no history behind it.
- The audit log is a change feed with a short memory. It records role and group changes, but it retains roughly 180 days, and it never records a roster. Reconstructing a past-date roster from change events means replaying every change since a known state you do not have.
- Access is indirect. A user is in a group, the group holds a project role, the role grants access. Any answer has to expand groups to effective users, at the point in time being asked about, which is the part the audit log cannot give you.
Longer retention is a long-open request on the Jira tracker (JRACLOUD-68267), and the access-history problem is a specific, sharper form of it.
The DIY approach: take a dated snapshot every day
If the platform will not remember, you record it yourself. The REST API has every piece:
- Enumerate projects, then each project's roles
(
GET /rest/api/3/project/{key}/role) and the actors in each role, users and groups alike. - Expand groups to people with
GET /rest/api/3/group/member, so the snapshot holds effective users, not just group names that will mean something different next year. - Store the whole matrix under today's date in storage you control, and read it back verbatim when an auditor names a date.
- Diff each run against the previous one so you also get a drift feed: who was added or removed, per project, per role, per day.
The catch is operational: this is a scheduled crawler with rate-limit handling, per-project failure isolation, a retention window, and pruning, and it has to run every day, forever. Miss a week and that week is unanswerable. The diff step is also where hand-rolled scripts usually go wrong, because group membership changes and role changes interleave.
A ready-made option: Access Snapshot & Drift
Access Snapshot & Drift takes that daily snapshot for you: every project's roles, group grants, and effective users, with groups fully expanded, stored under its date and retained for a window you control (around 400 days by default, well past the native cap). Each run is diffed against the last, so drift is a delta to review rather than a re-audit, and any date's full matrix exports as CSV, one row per grant. The snapshot and reporting path is read-only.
Key facts
- Native memory: live roster today, change events for about 180 days, no point-in-time roster ever.
- What evidence needs: a dated matrix of project, role, group, and effective user, readable for any date in the review period.
- Durable fix: a daily snapshot with groups expanded, retained past the audit horizon, diffed for drift.
- Least privilege: project, role, group, and user read; app storage. Nothing written to Jira on the snapshot path.
Related reading: How to keep Jira audit logs past the retention window and where groups are actually used in Jira.
Written by Abaho Katabarwa at Katabarwa Labs. We build small, single-purpose Atlassian and Azure tools that run entirely inside your own tenant. Questions: support@llmgraph.ai.