Automating and Fast-Tracking Approvals

Manual approvals are good, but automation is better.

πŸ“˜

This is a mini-guide

This page is intended to practically introduce a handful basic Sym concepts, all of which are covered elsewhere in greater detail.

This page includes references to:

This page builds on concepts from:

Overview

While Sym's get_approvers Reducer function will help you route your requests to the right places, 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. By returning the Template and a transition state, you can shortcut the full request cycle.

2377

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_approvers Reducer.

2801

The Sym state machine, with the on_request and get_approvers 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 workflow.
    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 invoke it in your on_request handler:

@hook
def on_request(event):
    """
    If this is an emergency request, then auto-approve the workflow.
    """
    fvars = event.flow.vars
    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. πŸŽ‰