Sym SDK Overview

Use Python to extend and customize your Sym Flows.

Overview

From routing requests, to deciding who can approve them, to running side-effecting Lambdas in your VPC, the Sym SDK gives you the tools to extend and automate your Sym Flows with custom logic and third-party integrations

The Sym SDK has two main components:

  • Integrations that interact with third party services.
  • Workflow Handlers that you can use to inject custom logic into your process.

Integrations, like PagerDuty, AWS Lambda, and Slack, are all covered in the Integrations section of our docs; Handlers are covered below.

The SDK's reference docs can be found at our here.

🚧

Restrictions Apply

Your custom Python code will be executed inside of a RestrictedPython environment, and as a result certain methods might not be available (e.g. all(), next()).

In addition, only imports from the following modules are allowed:

  • sym.sdk
  • requests
  • json
  • datetime

Integrations

The SDK exposes a set of integrations that connect to external services such as Slack and PagerDuty. Use integrations to check and manage memberships, send additional messages, or call out to external services.

📘

For information on integrating specific services, see our Integrations Overview.

Workflow Handlers

Under the hood, each Sym Flow consists of a series of discrete steps that can be interrupted or redirected with custom logic wrapped in Handler functions. The types of Handlers are reducers, hooks, and prefetch

Reducers are prefixed with get_, and take in an Event and return a single value.
Prefetch Reducers let you dynamically query for the data to show in a request field.
Hooks are prefixed with on_ or after_, and allow you to alter control flow by inserting custom logic before or after an Event is processed.

📘

The SDK's reference docs live elsewhere

Go here to see the full reference for the Sym SDK.

Handlers in context

Handlers fire at specific moments in a Sym Flow, giving you the power to define things like:

  • Where requests are routed
  • Scenarios in which requests are automatically approved or rejected
  • How to handle third-party identity lookups
  • What to do after a request cycle is completed

📘

Handlers are defined in the sym.sdk.annotations module.

Reducers

A Reducer injects key logic into Flows by taking an Event as input, and returning a single value.

For example, you will always need a way to let Sym know how to route a request and who is allowed to act on it. You may also want to inject some logic around whether a user is on-call in PagerDuty, or in a specific Okta group. This is exactly what the get_permissions and get_request_notifications Reducers are for:

from sym.sdk.annotations import reducer
from sym.sdk.integrations import okta, pagerduty, slack
from sym.sdk.notifications import Notification
from sym.sdk.request_permission import PermissionLevel, RequestPermission


def is_urgent(event):
    return (
        pagerduty.is_on_call(event.user, schedule_name="prod_on_call_schedule")
        or event.payload.fields["urgency"] == "High"
    )


@reducer
def get_permissions(event):
    return RequestPermission(
        # Only admins can view requests made by other users in the Sym web app.
        webapp_view=PermissionLevel.ADMIN,
        # Members and admins may approve or deny Sym requests.
        approve_deny=PermissionLevel.MEMBER,
        # Users may approve their own requests if they are on-call or mark the request as High urgency
        allow_self_approval=is_urgent(event),
    )


@reducer
def get_request_notifications(event):
    managers_okta_group = "00g12345678abc"

    if is_urgent(event):
        # Send a notification to the #break-glass channel; user would also be allowed to self-approve by
        # get_permissions Reducer.
        return [Notification(destinations=[slack.channel("#break-glass")])]
    else:
        # First send requests to #sym-requests. If they aren't actioned in 5 minutes, forward the request to #managers.
        # If still not actioned in a further 5 minutes, send a group DM to members of the "managers" Okta group.
        return [
            Notification(destinations=[slack.channel("#sym-requests")], timeout=300),
            Notification(destinations=[slack.channel("#managers")], timeout=300),
            Notification(destinations=[slack.group(okta.users_in_group(group_id=managers_okta_group))]),
        ]

Prefetch Reducers

Prefetch Reducers are the Sym SDK's method for dynamically populating fields in Slack and enabling typeahead for your users. Like normal Reducers, Prefetch Reducersr will take an Event. They also require an attribute representing a prompt_field to be displayed in Slack.

Then, you can fill in your own code or invoke a Lambda to fetch a set of options and return them to the prompt_field in Slack.

import requests
from sym.sdk.annotations import prefetch
from sym.sdk.field_option import FieldOption


@prefetch(field_name="targets")
def get_targets(event):
    # Make an API Call or even invoke an AWS Lambda
    response = requests.get(url="https://yourapi.foo/get-targets")
    targets = response.json()["results"]

    # Return a list of FieldOption
    return [
        FieldOption(value=target["name"], label=target["name"].upper())
        for target 
        in targets
    ]

Hooks

On-Hooks are executed before the default implementation of an Event handler in a Template. They offer an opportunity to bypass, short-circuit, or alter control flow, by emitting Events.

For example, you may want to auto-approve any requests that come from an on-call engineer:

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

# Hooks are optional, and can change control flow by returning Events

@hook
def on_request(event):
    # Auto-approve urgent requests for access by the person on call
    if event.payload.fields["urgency"] == "Urgent" and pagerduty.is_on_call(event.user):
        return ApprovalTemplate.approve()

After Hooks are executed after the default implementation of an Event handler in a Template. They offer an opportunity to execute additional side-effects, such as logging or notifications.

For example, you may want to log every approval to a private Slack channel:

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

# After-hooks are optional, and let you execute side-effects after an Event

@hook
def after_approve(event):
    """Executed after an approved event has been fired."""
    message = f"{event.user.username} has been approved for {event.payload.fields['target'].name}!"
    slack.send_message(slack.channel("#private-audit-log"), message)

Implementation guides and examples

See any of the guides below for guides and examples of Sym's SDK in action.

GuideDescriptionType
Customizing Flow LogicLearn how to customize Flow logic, including the basics of Hooks and Reducers.General information
Working With Flow Fields and DataLearn how to use field and Flow data from Terraform in your Python code.General information
Reducers: Routing and IdentityLearn about Reducers, which are helpful for routing Flows and managing use identity.SDK Reference
Hooks: Flow Control and AutomationLearn about Hooks, which help you control and automate your Flows.SDK Reference
Prefetch: Typeahead FieldsLearn about the special prefetch Reducer, which can be used to create custom typeahead fields.SDK Reference
Choosing Where Your Requests GoLearn how to conditionally route your requests.Implementation example
Automating and Fast-Tracking ApprovalsLearn how to automate, fast-track, and otherwise redirect requests.Implementation example
Defining Who Can Approve RequestsLearn how to manage who's allowed to approve and deny requests.Implementation example
More Implementation ExamplesA grab bag of additional example snippets.Implementation examples

What’s Next