Automating and Fast-Tracking Approvals

Manual approvals are good, but automation is better.

Overview

While Sym's get_permissions Reducer will help the right people see and act on your request, it won't answer the question of skipping human interaction altogether. For example:

  • On-call scenarios where you want to grant access without prior review
  • Requests from privileged or unprivileged groups where the answer will always be "yes" or "no."

For these situations, the Sym SDK provides Hooks that can be used to alter a Flow's control logic.

Concepts

You can control your approval Flow's path from impl.py

Sym provides access to the ApprovalTemplate itself, which is the state machine common to all Flows. You can shortcut the complete request cycle by returning the Template and a transition state.

A happy path along the Sym state machine

A happy path along the Sym state machine

For example:

from sym.sdk.templates import ApprovalTemplate

#...
	return ApprovalTemplate.approve(reason="You're approved!")
#...

Note: Template transitions can only move forward. In other words, you can move from on_request to ApprovalTemplate.approve, but you cannot move from on_escalate to ApprovalTemplate.deny.

The on_request hook is your friend in automation

The most common hook you'll use for Flow automation is on_request, which will fire before a request is routed via the get_permissions Reducer.

The Sym state machine, with the `on_request`, `get_permissions` and `get_request_destinations` steps highlighted

The Sym state machine, with the on_request, get_permissions and get_request_notifications steps highlighted

In the below example, every execution of this Flow will be approved automatically, and every user who runs this Flow will see Everything goes! as the given reason for the escalation:

from sym.sdk.annotations import hook
from sym.sdk.integrations import slack
from sym.sdk.templates import ApprovalTemplate

@hook
def on_request(event):
    """Auto-approve every request."""
    return ApprovalTemplate.approve(reason="Everything goes!")

Use Flow data and helper functions to drive your automations

Of course, you won't always want to approve everything. One easy way to handle this is to write a helper function that pulls in some information from your Flow execution and invokes it in your on_request handler:

@hook
def on_request(event):
    """If this is an emergency request, then auto-approve it."""
    if is_fast_tracked(event):
        target = event.payload.fields["target"]
        message = f"{event.user.email} was fast tracked {target.label} AWS access."
        return ApprovalTemplate.approve(reason=message)

def is_fast_tracked(event):
    """Determine if this request should be fast tracked or go through
    normal approval channels.
    """
    return event.payload.fields.get("urgency") == "Emergency"

Other applications

In addition to fast-tracking emergencies, other common applications of this pattern include:

  • Approving if the target is in a list that you would consider "low risk."
  • Checking whether someone is on-call via PagerDuty
  • Auto-approving or -denying based on an attribute like Okta group or OneLogin role
  • Custom logic based on the result of a custom AWS Lambda

And just like that, you can automate, fast-track, and auto-deny Flows based on custom logic. ๐ŸŽ‰