Skip to content

Build cloud applications in minutes instead of days

With Zephyr, even the most complex cloud systems can be made simple, elegant, and succinct.

hcl
provider "aws" {
  region = "us-east-1"
}

# Queue and dead-letter queue for widget updates
resource "aws_sqs_queue" "widget_updates_queue" {
    name = "results-updates-queue"
    redrive_policy  = "{\"deadLetterTargetArn\":\"${aws_sqs_queue.results_updates_dl_queue.arn}\",\"maxReceiveCount\":5}"
    visibility_timeout_seconds = 300

    tags = {
        Environment = "dev"
    }
}

resource "aws_sqs_queue" "results_updates_dl_queue" {
    name = "results-updates-dl-queue"
}

# IAM role and policy for POST /widgets lambda
resource "aws_iam_role" "post_lambda_role" {
name   = "Post_Lambda_Function_Role"
assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "iam_policy_for_post_lambda" {
  name         = "aws_iam_policy_for_terraform_aws_lambda_role"
  path         = "/"
  description  = "AWS IAM Policy for managing aws lambda role"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*",
      "Effect": "Allow"
    },
    {
      "Action": [
        "sqs:GetQueueAttributes",
        "sqs:SendMessage"
      ],
      "Effect": "Allow",
      "Resource": "${aws_sqs_queue.widget_updates_queue.arn}"
    }
  ]
}
EOF
}
  
resource "aws_iam_role_policy_attachment" "attach_iam_policy_to_iam_role" {
  role        = aws_iam_role.post_lambda_role.name
  policy_arn  = aws_iam_policy.iam_policy_for_post_lambda.arn
}

# Create the POST /widgets lambda
resource "aws_lambda_function" "post_widgets_func" {
  filename                       = "post-widgets/dist.zip"
  function_name                  = "Post_Widgets_Lambda_Function"
  role                           = aws_iam_role.post_lambda_role.arn
  handler                        = "index.lambda_handler"
  runtime                        = "python3.8"
  depends_on                     = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role]
}

# Create an API gateway and route POST /widgets to our lambda
resource "aws_apigatewayv2_api" "widgets_api" {
  name          = "serverless_lambda_gw"
  protocol_type = "HTTP"
}

resource "aws_apigatewayv2_stage" "prod" {
  api_id = aws_apigatewayv2_api.widgets_api.id

  name        = "serverless_lambda_stage"
  auto_deploy = true
}

resource "aws_apigatewayv2_integration" "post_widgets" {
  api_id = aws_apigatewayv2_api.widgets_api.id

  integration_uri    = aws_lambda_function.post_widgets_func.invoke_arn
  integration_type   = "AWS_PROXY"
  integration_method = "POST"
}

resource "aws_apigatewayv2_route" "post_widgets_func" {
  api_id = aws_apigatewayv2_api.widgets_api.id

  route_key = "POST /widgets"
  target    = "integrations/${aws_apigatewayv2_integration.post_widgets.id}"
}

resource "aws_lambda_permission" "api_gw" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.post_widgets_func.function_name
  principal     = "apigateway.amazonaws.com"

  source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*"
}

# IAM role and policy for lambda that updates reporting db
resource "aws_iam_role" "update_reporting_lambda_role" {
name   = "Update_Reporting_Function_Role"
assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_policy" "iam_policy_for_reporting_lambda" {
  name         = "aws_iam_policy_for_terraform_aws_lambda_role"
  path         = "/"
  description  = "AWS IAM Policy for managing aws lambda role"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*",
      "Effect": "Allow"
    },
    {
      "Action": [
        "sqs:ChangeMessageVisibility",
        "sqs:DeleteMessage",
        "sqs:GetQueueAttributes",
        "sqs:ReceiveMessage"
      ],
      "Effect": "Allow",
      "Resource": "${aws_sqs_queue.widget_updates_queue.arn}"
    }
  ]
}
EOF
}
  
resource "aws_iam_role_policy_attachment" "attach_iam_policy_to_iam_role" {
  role        = aws_iam_role.update_reporting_lambda_role.name
  policy_arn  = aws_iam_policy.iam_policy_for_reporting_lambda.arn
}

# Create the lambda that updates the reporting db
resource "aws_lambda_function" "update_reporting_func" {
  filename                       = "update-reporting/dist.zip"
  function_name                  = "Update_Reporting_Lambda_Function"
  role                           = aws_iam_role.update_reporting_lambda_role.arn
  handler                        = "index.lambda_handler"
  runtime                        = "python3.8"
  depends_on                     = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role]
}

# Event source from our queue into our reporting lambda
resource "aws_lambda_event_source_mapping" "event_source_mapping" {
  event_source_arn = "${aws_sqs_queue.widget_updates_queue.arn}"
  enabled          = true
  function_name    = "${aws_lambda_function.update_reporting_func.arn}"
  batch_size       = 1
}
zephyr
use aws::lambda

op addWidget(widget) = lambda!(
  #pkg::src('./operations/add-widget'), widget
)
op addToReportingDatabase(widget) = lambda!(
  #pkg::src('./operations/update-reporting'), widget
)

api 'widgets-service.example.com' = REST {
  POST /widgets widget = addWidget(widget)
}

addWidget(widget) --> addToReportingDatabase(widget)
Almost 200 lines of Terraform is only 14 lines of Zephyr.

Architecture-as-code

Human-friendly syntax for common patterns. Instead of writing infrastructure boilerplate, directly express your system design with high-level "architecture-as-code."

Learn More
zephyr
// Define operations
op removeOutliers(data)
op normalize(row)
op updateModel(data)

// Define events
evt NewData(data)

// Data pipeline
NewData(dataPoints) --> removeOutliers(dataPoints)
  // Map
  -=> [row] normalize(row): normalized
  // Collect
  =-> []collected updateModel(collected)
zephyr
// Define operations
op eventListener(x)
op otherListener(y)

// Define events
evt FooEvent(x, y)

api 'async-api.my-org.co' = REST {
  // POST endpoint that generates an event
  POST /foo { field1, field2 } --> FooEvent(
    field1, field2
  )
}

// Multiple asynchronous event listeners
FooEvent(field1, _) --> eventListener(field1)
FooEvent(_, field2) --> otherListener(field2)
zephyr
// Define operations
op createOrUpdateWidget(widget)

api 'api.my-org.co' = REST {
  // Map endpoints
  POST /widgets body = createOrUpdateWidget(body)
  PUT /widgets/:id body = createOrUpdateWidget({
    id, ...body
  })
  PATCH /widgets/:id body = createOrUpdateWidget({
    id, ...body
  })
}

Easy adoption, total flexibility

Use any language or tech stack. Integrates seamlessly with the tools you already use.

Learn More