Implement Your First Flow

Overview

By this point, you should have:

  • Installed the symflow CLI on your local machine
  • Installed the Sym Slack app into your organization's workspace
  • Declared a Slack Integration, Error Logger, Environment, and a Sym Runtime in Terraform using the Sym Terraform Provider

Which means you're ready to implement your first Flow! In this tutorial, we will implementing an Approval-Only Flow.

Declaring the Flow Resource

First thing's first, we need one last Terraform resource: the sym_flow. Creating this resource will let you use the Sym Slack app to make requests.

The Terraform configuration of our sym_flow can be broken down into three parts:

The Basics

The basic configuration of a sym_flow includes:

Field

Description

Required

name

A unique, human-readable identifier for the Flow. This can be used to run the Flow directly in Slack (e.g. /sym req approval)

Yes

label

The display name for the Flow. This is what you'll see in Slack.

No

template

The Template defines the steps of the Sym state machine. Currently, approval is the only option.

Yes

implementation

The path to a file where the Sym Python SDK will be used to customize the workflow. More on that later!

Yes

environment_id

What Environment this Flow belongs to. This can be helpful to separate Flows you're still iterating on and testing from Flows that are stable and used every day by your organization. Think "main" vs. "sandbox" or "prod" vs. "staging".

Yes

resource "sym_flow" "this" {
  name  = "approval"
  label = "Approval"

  template       = "sym:template:approval:1.0.0"
  implementation = "${path.module}/impl.py"
  environment_id = sym_environment.this.id

  ...
}
11721172

name, label, and environment are surfaced in the Slack modal.

The Prompt Fields

Different types of access requests need different types of data. That's why Flows support custom fields in the request modal! We've kept it fairly basic here with just two generic questions— "what" and "why"— but fields can be as specific as needed. Check out Prompt Fields for more information.

resource "sym_flow" "this" {
  ...

  params = {
    # prompt_fields_json defines custom form fields for the Slack modal that
    # requesters fill out to make their requests.
    prompt_fields_json = jsonencode([
      {
        name     = "resource"
        label    = "What do you need access to?"
        type     = "string"
        required = true
      },
      {
        name     = "reason"
        label    = "Why do you need access?"
        type     = "string"
        required = true
      }
    ])
  }
}
11661166

prompt_fields_json becomes form fields for your request modal!

The Variables

Finally, we have the Flow vars. These are variables to pass into your Flow which can be used in the impl.py. This is also crucial if you need to pass in IDs dynamically generated by Terraform resources, such as the ARN of a AWS Lambda.

We've started out with just a couple simple variables, defined in terraform.tfvars:

flow_variables = {
  request_channel = "#sym-requests"              # Slack Channel where requests should go
  approvers       = "[email protected],[email protected]"  # Safelist of users that can approve requests
}

The impl.py

Now that the sym_flow resource is configured, all that's left is to write its impl.py. This is where Sym's Python SDK can be used to customize your Flow's logic.

The first thing we'll need in the impl.py is a get_approvers reducer. This is the only required part of an impl.py and it tells Sym where to send access requests in Slack:

# First, we'll import everything we need for this impl.py
from sym.sdk.annotations import hook, reducer
from sym.sdk.integrations import slack
from sym.sdk.templates import ApprovalTemplate

# Then, we add our first reducer! `get_approvers` will be used any time a request
# is made in Sym.
@reducer
def get_approvers(event):
    """Route Sym requests to a channel specified in the sym_flow."""

    flow_vars = event.flow.vars  # This is how we get the sym_flow.vars we set above

    # Returning a Slack channel object will tell Sym where to send the request.
    # allow_self then lets the requester approve themself, which is great for testing!
    return slack.channel(flow_vars["request_channel"], allow_self=True)

While get_approvers is the only required function in the impl.py, there are a couple basic, commonly used hooks: on_approve and on_deny.

These hooks get run when the "Approve" and "Deny" buttons are clicked, before the event is let through, so they have the chance to reject or modify events based on custom logic. This provides total control over who can approve or deny requests.

@hook
def on_approve(event):
    """Only let members of the approver safelist approve requests."""

    # Use a helper function for common logic across hooks!
    if not has_approve_access(event):
        # Return an "ignore" event that will stop the "approve" event (leaving the
        # request's state unchanged) and send a DM to the user who clicked "Approve"
        # letting them know they're not allowed to approve the request.
        return ApprovalTemplate.ignore(
            message="You are not authorized to approve this request."
        )


@hook
def on_deny(event):
    """Only let members of the approve safelist or the original requester
    deny requests.
    """
    # The Sym User that made the request
    requester = event.get_actor("request")

    # event.user is the user who just clicked the "Deny" button
    if not (requester == event.user or has_approve_access(event)):
        return ApprovalTemplate.ignore(
            message="You are not authorized to deny this request."
        )


def has_approve_access(event):
    """Check if the requesting user is in the safelist, defined in the sym_flow."""

    flow_vars = event.flow.vars
    approvers = flow_vars["approvers"].split(",")
    return event.user.username in approvers

Making it Real

Almost there! All the Terraform configuration is now written, all that's left to do is test it out! We'll start by applying the Terraform:

$ terraform init

Initializing the backend...
Initializing provider plugins...
Terraform has been successfully initialized!

$ terraform apply

sym_runtime.this: Creating...
sym_integration.slack: Creating...
sym_runtime.this: Creation complete after 1s [id=c1d119ed-9618-4b86-95c9-bc5702c04acc]
sym_integration.slack: Creation complete after 1s [id=2127a917-f92b-491a-ba63-6556734b152c]
sym_error_logger.slack: Creating...
sym_error_logger.slack: Creation complete after 0s [id=358fa690-c5bb-457e-9527-ef5aad12a542]
...

Once the terraform apply is done:

  1. Go to the Slack workspace you installed Sym in
  2. Type /sym anywhere

and you should see your very first Sym Flow, ready for use!

581581

Next Steps

Nice job! You've made it to the end of this quickstart, which means you should have a basic Sym Approval-Only Flow up and running. Now that you've got the basics down, there are a ton of ways to tailor Sym to your organization's needs. To learn more, check out:

  • Integrating Services - Learn how Sym can automatically grant or escalate access in external systems
  • Our Python SDK - Learn how to customize the impl.py to fit your specific rules
  • Reporting - Learn how to get structured logs for all access requests and set your organization up for audit success
  • Examples - Check out end-to-end examples of some common Flows

Did this page help you?