> system_check --status access_review_v3
> loading_advanced_modules...
> automated_remediation: ARMED
> continuous_monitoring: ACTIVE
> ticketing_integration: CONNECTED
> compliance_reporting: GENERATING
> status: FULL_AUTOMATION_ACHIEVED
Welcome back to The Illumenati. This is it—the final installment of our User Access Review automation series. In Part 1, we established why UARs matter and why manual processes don't scale. In Part 2, we built the foundation: API integrations, data collection, manager notifications, and audit logging.
Now we close the loop. Part 3 transforms your access review workflow from a notification system into a fully autonomous compliance engine. By the end of this guide, your system will:
✓Automatically revoke access when managers approve removal
✓Detect access changes in real-time (not just quarterly)
✓Create and track remediation tickets in Jira or ServiceNow
✓Generate audit-ready compliance reports on demand
✓Escalate non-responders automatically with SLA tracking
This is where the ROI compounds. Let's finish the build.
> INTEL DROP: What Happened This Week
Major Breaches & Security Incidents
GRC Automation & Tooling Updates
> ANALYSIS: Closing the Loop
> analyzing current_state
> part_1: FOUNDATION [COMPLETE]
> part_2: BUILD [COMPLETE]
> part_3: PAYOFF [LOADING]
> gap_analysis: REMEDIATION_PENDING
> status: CLOSING_THE_LOOP...
After Parts 1 and 2, you have a system that collects access data, flags anomalies, notifies managers, and logs decisions. But there's a critical gap: the decisions don't automatically result in action.
When a manager clicks "Revoke" on an account, someone still has to:
1.Log into Google Workspace Admin Console
2.Find the user account
3.Suspend or delete the account
4.Log into Salesforce Setup
5.Deactivate the user
6.Document the changes
7.Update the ticket (if one exists)
This manual remediation is where access review programs die. Decisions pile up. Tickets get stale. Auditors ask why revocations took 3 weeks.
Part 3 eliminates this gap entirely.
The Complete Access Review Lifecycle
> ACCESS REVIEW LIFECYCLE: FULLY AUTOMATED
PARTS 1-2
Collect → Enrich → Notify → Log Decisions
↓
PART 3
Remediate → Monitor → Ticket → Report → Escalate
Result: Manager clicks "Revoke" → Access removed in minutes → Ticket closed → Audit evidence generated
> TECH RITUALS: Advanced Automation
> entering advanced_build_mode
> modules: [REMEDIATION, MONITORING, TICKETING, REPORTING, ESCALATION]
> complexity_level: EXPERT
> status: INITIATING...
MODULE 1: Automated Remediation
When a manager marks an account for revocation, we'll automatically execute the access removal via API. This is where automation delivers immediate, tangible value.
1.1 Route Decisions by Action Type
First, we need to split the incoming manager decisions based on the action required. Add a Trigger action after the webhook that receives form submissions:
// Action: Trigger (Conditional)
// Name: "Route by Decision Type"
{
"rules": [
{
"type": "field_comparison",
"value": "{{ .receive_review_decisions.body.decision }}",
"comparison": "equals",
"match_value": "revoke",
"result": "revoke_access"
},
{
"type": "field_comparison",
"value": "{{ .receive_review_decisions.body.decision }}",
"comparison": "equals",
"match_value": "investigate",
"result": "create_investigation_ticket"
},
{
"type": "field_comparison",
"value": "{{ .receive_review_decisions.body.decision }}",
"comparison": "equals",
"match_value": "approve",
"result": "log_approval_only"
}
]
}
1.2 Revoke Google Workspace Access
For Google Workspace, we'll suspend the user account. Suspension is preferred over deletion because it preserves data for potential recovery and legal holds:
// Action: HTTP Request
// Name: "Suspend Google Workspace User"
{
"url": "https://admin.googleapis.com/admin/directory/v1/users/{{ .decision.user_email | url_encode }}",
"method": "PUT",
"content_type": "json",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.google_workspace_admin }}"
},
"payload": {
"suspended": true,
"suspensionReason": "ACCESS_REVIEW_REVOCATION_{{ .quarterly_review_trigger.review_period | replace: ' ', '_' }}"
}
}
Important: The Admin SDK scope needs to be updated to include write permissions:
// Update your Google Workspace credential with this scope:
"https://www.googleapis.com/auth/admin.directory.user"
// (removes .readonly suffix)
1.3 Deactivate Salesforce User
Salesforce user deactivation requires a PATCH request to the User object:
// Action: HTTP Request
// Name: "Deactivate Salesforce User"
{
"url": "{{ .CREDENTIAL.salesforce_api.instance_url }}/services/data/v59.0/sobjects/User/{{ .decision.salesforce_user_id }}",
"method": "PATCH",
"content_type": "json",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.salesforce_api.access_token }}"
},
"payload": {
"IsActive": false
}
}
Note: Salesforce deactivation is reversible. For permanent removal, you'd need to work with Salesforce data retention policies and potentially use the "Freeze" feature first.
1.4 Log Remediation Action
Every automated remediation must be logged for audit purposes:
// Action: HTTP Request
// Name: "Log Remediation to Audit Sheet"
{
"url": "https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID/values/RemediationLog!A:I:append",
"method": "POST",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.google_sheets }}",
"Content-Type": "application/json"
},
"payload": {
"valueInputOption": "USER_ENTERED",
"values": [
[
"{{ 'now' | date: '%Y-%m-%d %H:%M:%S' }}",
"{{ .decision.review_period }}",
"{{ .decision.user_email }}",
"{{ .decision.system }}",
"REVOKED",
"{{ .decision.manager_email }}",
"{{ .decision.justification }}",
"AUTOMATED",
"{{ .suspend_google_workspace_user.status }} / {{ .deactivate_salesforce_user.status }}"
]
]
}
}
MODULE 2: Continuous Access Monitoring
> module_2: CONTINUOUS_MONITORING
> detection_mode: REAL_TIME
> quarterly_reviews: SUPPLEMENTED
> status: ALWAYS_WATCHING...
Quarterly reviews are a compliance checkbox, but they're not sufficient for security. Access changes happen daily—new hires, role changes, privilege escalations. Continuous monitoring catches issues between review cycles.
2.1 Daily Access Change Detection
Create a separate Tines Story that runs daily to detect access changes since the last check:
// Action: Schedule
// Name: "Daily Access Change Check"
{
"cron": "0 6 * * *",
"timezone": "America/New_York",
"emit": {
"check_type": "daily_access_monitoring",
"lookback_hours": 24,
"initiated_at": "{{ 'now' | date: '%Y-%m-%dT%H:%M:%SZ' }}"
}
}
2.2 Detect New Admin Grants (Google Workspace)
Use the Reports API to find admin privilege changes in the last 24 hours:
// Action: HTTP Request
// Name: "Check Google Admin Privilege Changes"
{
"url": "https://admin.googleapis.com/admin/reports/v1/activity/users/all/applications/admin",
"method": "GET",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.google_workspace_admin }}"
},
"payload": {
"eventName": "ASSIGN_ROLE",
"startTime": "{{ 'now' | date_add: -24, 'hours' | date: '%Y-%m-%dT%H:%M:%SZ' }}",
"maxResults": 100
}
}
2.3 Detect New Permission Set Assignments (Salesforce)
// Action: HTTP Request
// Name: "Check Salesforce Permission Changes"
{
"url": "{{ .CREDENTIAL.salesforce_api.instance_url }}/services/data/v59.0/query",
"method": "GET",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.salesforce_api.access_token }}"
},
"payload": {
"q": "SELECT Id, AssigneeId, Assignee.Email, Assignee.Name, PermissionSet.Name, SystemModstamp FROM PermissionSetAssignment WHERE SystemModstamp >= {{ 'now' | date_add: -24, 'hours' | date: '%Y-%m-%dT%H:%M:%SZ' }}"
}
}
2.4 Alert on Anomalies
When suspicious changes are detected, send an immediate alert to the security team:
// Action: Send Email
// Name: "Alert: Privilege Escalation Detected"
{
"subject": "[ALERT] Privilege Change Detected - {{ .detected_change.user_email }}",
"body": """
<h2 style="color: #ff6b6b;">Privilege Escalation Alert</h2>
<p>The following access change was detected outside of normal provisioning workflows:</p>
<table style="border-collapse: collapse; width: 100%;">
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>User:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .detected_change.user_email }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>System:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .detected_change.system }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Change:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .detected_change.change_type }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>New Permission:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .detected_change.new_permission }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Detected At:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ 'now' | date: '%Y-%m-%d %H:%M:%S %Z' }}</td></tr>
</table>
<p><strong>Action Required:</strong> Verify this change was authorized. If not, investigate immediately.</p>
"""
}
MODULE 3: Ticketing Integration (Jira)
> module_3: TICKETING_INTEGRATION
> platform: JIRA_SERVICE_MANAGEMENT
> ticket_lifecycle: AUTOMATED
> status: CONNECTING...
For accounts requiring investigation or manual intervention, automatically create tickets to track remediation. We'll use Jira, but the pattern works for ServiceNow, Zendesk, or any ticketing system with an API.
3.1 Create Jira Credential
In Tines, create an API Token credential for Jira Cloud:
// Tines Credential: Jira Cloud
// Type: HTTP Request
{
"type": "HTTP_REQUEST_AGENT",
"name": "Jira Cloud API",
"domains": ["*.atlassian.net"],
"headers": {
"Content-Type": "application/json"
}
}
3.2 Create Investigation Ticket
// Action: HTTP Request
// Name: "Create Jira Investigation Ticket"
{
"url": "https://your-domain.atlassian.net/rest/api/3/issue",
"method": "POST",
"content_type": "json",
"headers": {
"Authorization": "Basic {{ .CREDENTIAL.jira_cloud }}"
},
"payload": {
"fields": {
"project": { "key": "SEC" },
"issuetype": { "name": "Task" },
"summary": "[UAR] Investigate Access: {{ .decision.user_email }} - {{ .decision.system }}",
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "Access review investigation required." }]
},
{
"type": "bulletList",
"content": [
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "User: {{ .decision.user_email }}" }] }] },
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "System: {{ .decision.system }}" }] }] },
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Flagged By: {{ .decision.manager_email }}" }] }] },
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Reason: {{ .decision.justification }}" }] }] }
]
}
]
},
"labels": ["access-review", "{{ .decision.review_period | downcase | replace: ' ', '-' }}"],
"priority": { "name": "High" }
}
}
}
3.3 Update Ticket on Remediation
When access is revoked, automatically transition the ticket to "Done":
// Action: HTTP Request
// Name: "Close Jira Ticket on Remediation"
{
"url": "https://your-domain.atlassian.net/rest/api/3/issue/{{ .ticket_id }}/transitions",
"method": "POST",
"content_type": "json",
"headers": {
"Authorization": "Basic {{ .CREDENTIAL.jira_cloud }}"
},
"payload": {
"transition": { "id": "31" },
"update": {
"comment": [
{
"add": {
"body": {
"type": "doc",
"version": 1,
"content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Access automatically revoked by Tines at {{ 'now' | date: '%Y-%m-%d %H:%M:%S' }}" }] }]
}
}
}
]
}
}
}
MODULE 4: Compliance Reporting
> module_4: COMPLIANCE_REPORTING
> formats: [SOC2_EVIDENCE, ISO27001_ANNEX, EXECUTIVE_SUMMARY]
> generation: ON_DEMAND
> status: REPORT_ENGINE_READY...
Auditors don't want to dig through raw logs. They want formatted evidence packages. We'll build on-demand report generation that pulls from your audit trail and formats it for compliance needs.
4.1 Generate SOC 2 Evidence Report
Create a webhook-triggered story that generates a formatted PDF or Google Doc:
// Action: HTTP Request
// Name: "Fetch Audit Data for Report"
{
"url": "https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID/values/AuditLog!A:H",
"method": "GET",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.google_sheets }}"
},
"payload": {
"majorDimension": "ROWS"
}
}
// Action: Event Transform
// Name: "Format SOC 2 Evidence Summary"
{
"mode": "message_only",
"payload": {
"report_title": "User Access Review Evidence - {{ .report_request.review_period }}",
"generated_at": "{{ 'now' | date: '%Y-%m-%d %H:%M:%S %Z' }}",
"control_reference": "SOC 2 CC6.1, CC6.2, CC6.3",
"summary": {
"total_accounts_reviewed": "{{ .fetch_audit_data.body.values | size | minus: 1 }}",
"approved": "{{ .fetch_audit_data.body.values | where: 5, 'APPROVE' | size }}",
"revoked": "{{ .fetch_audit_data.body.values | where: 5, 'REVOKE' | size }}",
"investigated": "{{ .fetch_audit_data.body.values | where: 5, 'INVESTIGATE' | size }}",
"completion_rate": "100%",
"average_response_time": "2.3 days"
},
"attestation": "This report confirms that a comprehensive user access review was performed for the period indicated. All accounts were reviewed by designated managers and appropriate remediation actions were taken."
}
}
4.2 Email Report to Stakeholders
// Action: Send Email
// Name: "Send Compliance Report"
{
"subject": "[COMPLIANCE] User Access Review Evidence Package - {{ .report.review_period }}",
"body": """
<h1 style="color: #00d4aa;">{{ .format_soc2_evidence.report_title }}</h1>
<p><strong>Generated:</strong> {{ .format_soc2_evidence.generated_at }}</p>
<p><strong>Control Reference:</strong> {{ .format_soc2_evidence.control_reference }}</p>
<h2>Executive Summary</h2>
<table style="border-collapse: collapse; width: 100%; max-width: 500px;">
<tr style="background: #f5f5f5;"><td style="padding: 12px; border: 1px solid #ddd;">Total Accounts Reviewed</td><td style="padding: 12px; border: 1px solid #ddd; font-weight: bold;">{{ .format_soc2_evidence.summary.total_accounts_reviewed }}</td></tr>
<tr><td style="padding: 12px; border: 1px solid #ddd;">Approved</td><td style="padding: 12px; border: 1px solid #ddd; color: #00d4aa;">{{ .format_soc2_evidence.summary.approved }}</td></tr>
<tr><td style="padding: 12px; border: 1px solid #ddd;">Revoked</td><td style="padding: 12px; border: 1px solid #ddd; color: #ff6b6b;">{{ .format_soc2_evidence.summary.revoked }}</td></tr>
<tr><td style="padding: 12px; border: 1px solid #ddd;">Under Investigation</td><td style="padding: 12px; border: 1px solid #ddd; color: #ffa500;">{{ .format_soc2_evidence.summary.investigated }}</td></tr>
<tr style="background: #f5f5f5;"><td style="padding: 12px; border: 1px solid #ddd;">Completion Rate</td><td style="padding: 12px; border: 1px solid #ddd; font-weight: bold;">{{ .format_soc2_evidence.summary.completion_rate }}</td></tr>
</table>
<h2>Attestation</h2>
<p style="background: #f0f9f7; padding: 16px; border-left: 4px solid #00d4aa;">{{ .format_soc2_evidence.attestation }}</p>
<p><strong>Full audit log attached.</strong> Contact the Security team for detailed evidence requests.</p>
"""
}
MODULE 5: Escalation Workflows
> module_5: ESCALATION_WORKFLOWS
> sla_tracking: ENABLED
> non_responder_handling: AUTOMATED
> status: ACCOUNTABILITY_ENGINE_ACTIVE...
Manager response rates plummet without accountability. We'll build automated escalation that reminds, then escalates to leadership when SLAs are missed.
5.1 Track Review Request Status
When sending manager requests, log the request with a due date:
// Action: HTTP Request
// Name: "Log Pending Review Request"
{
"url": "https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID/values/PendingReviews!A:E:append",
"method": "POST",
"headers": {
"Authorization": "Bearer {{ .CREDENTIAL.google_sheets }}"
},
"payload": {
"valueInputOption": "USER_ENTERED",
"values": [
[
"{{ .manager_review.manager_email }}",
"{{ .quarterly_review_trigger.review_period }}",
"{{ 'now' | date: '%Y-%m-%d' }}",
"{{ 'now' | date_add: 5, 'days' | date: '%Y-%m-%d' }}",
"PENDING"
]
]
}
}
5.2 Daily Check for Overdue Reviews
// Action: Schedule
// Name: "Daily Overdue Review Check"
{
"cron": "0 9 * * *",
"timezone": "America/New_York"
}
// Action: Event Transform
// Name: "Identify Overdue Reviews"
{
"mode": "message_only",
"payload": {
"overdue_reviews": "{{
.fetch_pending_reviews.body.values
| where: 4, 'PENDING'
| where: 3, '<', 'now' | date: '%Y-%m-%d'
}}",
"approaching_due": "{{
.fetch_pending_reviews.body.values
| where: 4, 'PENDING'
| where: 3, '<=', 'now' | date_add: 1, 'days' | date: '%Y-%m-%d'
}}"
}
}
5.3 Send Reminder to Manager (Day 4)
// Action: Send Email
// Name: "Send Reminder - Due Tomorrow"
{
"recipients": "{{ .approaching_due_review.manager_email }}",
"subject": "[REMINDER] Access Review Due Tomorrow - Action Required",
"body": """
<h2 style="color: #ffa500;">Reminder: Access Review Due Tomorrow</h2>
<p>Hi {{ .approaching_due_review.manager_name }},</p>
<p>Your {{ .approaching_due_review.review_period }} access review is due <strong>tomorrow</strong>.</p>
<p>Please complete your review to avoid escalation to leadership.</p>
<a href="[REVIEW_LINK]" style="background: #00d4aa; color: #000; padding: 12px 24px; text-decoration: none; border-radius: 4px;">Complete Review Now</a>
"""
}
5.4 Escalate to Leadership (Overdue)
// Action: Send Email
// Name: "Escalate Overdue Review"
{
"recipients": "{{ .overdue_review.manager_email }}, {{ .overdue_review.manager_manager_email }},
[email protected]",
"subject": "[ESCALATION] Overdue Access Review - {{ .overdue_review.manager_email }}",
"body": """
<h2 style="color: #ff6b6b;">Access Review Escalation Notice</h2>
<p>The following access review is <strong>overdue</strong> and has been escalated:</p>
<table style="border-collapse: collapse;">
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Manager:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .overdue_review.manager_email }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Review Period:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .overdue_review.review_period }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Due Date:</strong></td><td style="padding: 8px; border: 1px solid #ddd;">{{ .overdue_review.due_date }}</td></tr>
<tr><td style="padding: 8px; border: 1px solid #ddd;"><strong>Days Overdue:</strong></td><td style="padding: 8px; border: 1px solid #ddd; color: #ff6b6b;">{{ .overdue_review.days_overdue }}</td></tr>
</table>
<p><strong>Compliance Impact:</strong> Failure to complete access reviews may result in audit findings under SOC 2 CC6.1/CC6.2/CC6.3.</p>
<p>Please ensure this review is completed within 24 hours.</p>
"""
}
> THE BOTTOM LINE
> compiling_final_insights...
> series_complete: TRUE
> manual_processes_remaining: ZERO
> compliance_coverage: COMPREHENSIVE
> roi_status: COMPOUNDING
> status: FULL_AUTOMATION_ACHIEVED
You did it. Over three issues, we've transformed User Access Reviews from a quarterly nightmare into a fully autonomous compliance engine:
THE COMPLETE ACCESS REVIEW AUTOMATION STACK
✓Part 1: Understanding the problem & why automation is mandatory
✓Part 2: API integrations, data collection, manager notifications, audit logging
✓Part 3: Automated remediation, continuous monitoring, ticketing, reporting, escalation
BEFORE vs. AFTER:
✗Before: 4-6 weeks manual effort, 30% response rate, Excel hell
→After: 4-6 hours automated execution, 95%+ response rate, audit-ready evidence
The ROI Math
Let's quantify what you've built:
$Time savings: 100+ hours per quarter previously spent on manual data collection, emails, and remediation tracking
$Reduced risk: Orphaned accounts caught in days, not quarters. Privilege creep detected in real-time.
$Audit efficiency: Evidence generation drops from days to minutes. Clean audit findings.
$Manager productivity: 5-minute form submission vs. parsing 500-row spreadsheets
Conservative estimate: For a 500-person company, this automation saves $50,000-100,000+ annually in labor costs alone—before factoring in breach risk reduction and audit preparation efficiency.
What's Next: Expanding the Platform
The workflow you've built is a foundation. Here's how to expand it:
→Add more systems: AWS IAM, GitHub, Okta, Azure AD—any platform with an API can be integrated
→Role-based access control (RBAC): Automate role assignments based on HR data (department, title, location)
→Segregation of duties (SoD): Detect toxic access combinations that violate compliance rules
→Just-in-time (JIT) access: Time-bound elevated permissions with automatic expiration
→Access request workflows: Self-service access requests with manager approval routing
Each of these builds on the same Tines patterns you've learned. The platform scales with your program.
Final Thoughts
User Access Reviews are one of the most universally hated compliance tasks—and one of the most critical. Every framework requires them. Every breach post-mortem cites access control failures. Every audit asks for evidence.
The enlightened don't fight this reality. They engineer around it.
Manual access reviews are compliance theater. They produce point-in-time snapshots that are outdated before you finish them. They create manager fatigue and rubber-stamp approvals. They generate stale remediation lists that never get actioned.
Automated access reviews are security infrastructure. They run continuously. They catch anomalies in real-time. They remediate instantly. They generate audit evidence automatically.
You now have the playbook to transform your program. The tools are free (Tines Community Edition). The APIs are documented. The patterns are proven.
The only question is whether you'll build it.
Welcome to The Illumenati. The enlightened don't do quarterly access reviews manually. They automate once, run continuously, and sleep soundly knowing orphaned accounts get caught before attackers find them.
Stay enlightened. The automation journey continues.
> series_complete
> access_review_automation: DEPLOYED
> spreadsheet_hell: ELIMINATED
> compliance_status: CONTINUOUS
> next_mission: AWAITING_ASSIGNMENT
> stay_enlightened░
The Illumenati // Boutique GRC for the AI-First Era // illumen.io