Stacktape

Sign up



Directives

Introduction

  • YAML is a configuration language. It's declarative, clear, concise and brief. We believe using YAML is the best way to write configurations. However, being a configuration language, it doesn't allow you to include dynamic behavior. This is why Stacktape supports directives

  • Directives are functions that can be used within a template. They can be used to add dynamic behavior to the templates.

  • Stacktape supports 2 types of directives:

    1. Built-in directives: Built into Stacktape. Used for the most common jobs (e.g. getting command-line arguments, formatting a string)
    2. Custom directives: User-defined directives. Used to add custom logic (e.g. setting database instance size based on a stage or fetching data required in configuration). Custom directives can be written in Javascript, Typescript or Python.

Using directives

  • Directives always start with $.

  • Directive can be used to configure any property defined in the configuration.

    Copy

    myProperty: $MyDirective()

  • Directive can return:

    • primite values - strings, numbers or booleans.
    • objects and arrays - Accesing a property is done using a dot notation: $Directive().propertyName.
  • Directive can accept any number of arguments.

    • If you are passing arguments in-line, these arguments MUST be primitive values (not objects).
    • If you pass arguments as a result of another directives, these arguments can be primitive values, objects or arrays.

    Copy

    myProperty: $MyDirective('myParameter', 3, true)

  • Directives can be nested. However, there are 2 limitations:

    • Result of a directive resolved at runtime cannot be passed as an argument to a directive resolved locally.
    • Maximum nesting depth within a template is 2. $MyDirective1($MyDirective2()) is valid. $MyDirective1($MyDirective2($MyDirective2())) is not. To overcome this limitation, you can save the output of a directives to a variable, in the variables section of your configuration.

    Copy

    myProperty: $MyDirective1($MyDirective2())

Built-in directives

  • Used for the most common jobs, e.g. getting command-line arguments, formatting a string, etc.
  • Built-in directives can be resolved in 2 ways: locally or at runtime.

Local directives

  • The directive is completely resolved when the configuration is loaded. The directive body is substituted with the value returned from the directive.

$CliArgs()

  • Returns command-line arguments passed to the Stacktape command.

Copy

serviceName: my-service
stackConfig:
tags:
- name: stage
value: $CliArgs().stage

$File()

  • Reads and parses the content of a specified file.
  • Supported file types are:

dotenv files

  • Returns the parsed contents of the specified .env.* (dotenv) file.

Copy

myvar1=value1

Copy

resources:
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my-lambda.ts
environment:
- name: MY_DIRECTIVE_VARIABLE
value: $File('.env.staging').myvar1

ini files

  • Returns the parsed contents of the specified *.ini file.

Copy

myvar1=value1

Copy

resources:
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my-lambda.ts
environment:
- name: MY_DIRECTIVE_VARIABLE
value: $File('file.ini').myvar1

JSON files

  • Returns the parsed contents of the specified *.json file.

Copy

{ "myvar1": "value1" }

Copy

resources:
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my-lambda.ts
environment:
- name: MY_DIRECTIVE_VARIABLE
value: $File('file.json').myvar1

YML files

  • Returns the parsed contents of the specified *.yml file.

Copy

myvar1: value1

Copy

resources:
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my-lambda.ts
environment:
- name: MY_DIRECTIVE_VARIABLE
value: $File('file.yml').myvar1

$Format()

  • Returns an interpolated string.

  • First argument is a string to be interpolated. Every {} sub-string in this string will be substituted by the values passed as arguments 2-∞. There must be the same amount of {} as the number of arguments 2-∞.

  • $Format() directive cannot contain directives resolved at runtime as arguments. For these use-cases, use the $CfFormat().

  • Examples:

    • Directive $Format('{}-{}', 'foo', 'bar') will result in foo-bar
    • Directive $Format('{}-mydomain.com', 'foo') will result in foo-mydomain.com
    • Directive $Format('{}.{}', $Stage(), 'mydomain.com') will result in staging.mydomain.com given the stage is set to staging.

$Var()

  • Returns variable specified in the variables section.

  • Variables are used to:

    • reference values that are used more than once in the template
    • better organize your template files
    • save intermediate return values of other directives. This can be used to allow multi-level nesting of directives.

Copy

variables:
eventName: myCustomEventName
resources:
myEventBus:
type: 'event-bus'
# publishes events with EventName set to $Var().eventName into myEventBus
myPublisherFunction:
type: 'function'
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: 'lambdas/event-bus-publisher.ts'
environment:
- name: EVENT_NAME
value: $Var().eventName
accessControl:
allowAccessTo:
- myEventBus
# listens for events with EventName set to $Var().eventName published into myEventBus
myConsumerFunction:
type: 'function'
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: 'lambdas/consumer.ts'
events:
- type: event-bus
properties:
eventBusName: myEventBus
eventPattern:
detail:
EventName:
- $Var().eventName

$Stage()

  • Returns stage.
  • Unlike using $CliArgs().stage, this directive also resolves default values (configured using defaults:configure command).

$Region()

  • Returns region.
  • Unlike using $CliArgs().region, this directive also resolves default values (configured using defaults:configure command).

$Profile()

  • Returns profile.
  • Unlike using $CliArgs().profile, this directive also resolves default values (configured using defaults:configure command).

$This()

  • Returns the current stacktape template as an object.

Copy

serviceName: my-service
resources:
myHttpApi:
type: 'http-api-gateway'
properties:
customDomains:
# we are interpolating serviceName using This directive
- domainName: $Format('{}-{}', $This().serviceName, 'mydomain.com')

$GitInfo()

  • Returns the information about the current git repository, git user, etc.

Directive usageDescription
$GitInfo().sha1Return SHA-1 of the latest commit
$GitInfo().commitReturn the latest commit ID
$GitInfo().branchReturns the name of the current branch
$GitInfo().messageReturns the message of the last commit
$GitInfo().userReturns git user's name
$GitInfo().emailReturns git user's email
$GitInfo().repositoryReturns the name of the git repository
$GitInfo().tagsReturns the tags pointing to the current commit
$GitInfo().describeReturn the most recent tag that is reachable from a commit

Copy

serviceName: my-service
stackConfig:
tags:
- name: stage
value: $GitInfo().repository

$StackOutput()

  • Returns the specified output of another stack.
  • Used to reference parameters of resources deployed in a different stack.
  • Arguments
    • stack name - name of the stack that contains the output. Note that if the stack was deployed using Stacktape, stackName has the following format: <<serviceName>>-<<stage>>.
    • output name - name of the output to return.

$StackOutput() directive returns the output value of another stack when the current stack is being deployed.

To ensure the value won't change, or that the other stack won't get deleted, use the $CfStackOutput() directive


Consider we have deployed stack base-stack with the following configuration to a dev stage.

Copy

serviceName: base-stack
stackConfig:
outputs:
- name: bucketName
value: $ResourceParam('baseBucket', 'arn')
resources:
baseBucket:
type: bucket


We can now reference output of the base-stack in another stack:

Copy

serviceName: service-stack
variables:
baseStackName: $Format('{}-{}', 'base-stack', $Stage())
scripts:
validateBucket:
executeScript: hooks/validate-bucket-in-base-stack.ts
environment:
- name: BUCKET_NAME
value: $StackOutput($Var().baseStackName, 'baseBucketName')
hooks:
- triggers: ["before:deploy"]
scriptName: validateBucket
resources: ...

Runtime directives

  • Some directives are not resolved immediately (locally by Stacktape).
  • Example: To pass a connectionString of your database to an environment variable of your container, you can use $ResourceParam('myDatabase', 'connectionString') directive. The connection string is known only after the database has been created, and therefore can't be resolved before the first-time deployment. Instead, the resolution of the directive is handled by AWS CloudFormation during the deployment. Under the hood, the directives are transformed into CloudFormation intrinsic functions.
  • For convenience, when using runtime directives in post deploy hooks or when running cw:run-local command, the results of runtime directives will be automatically downloaded and resolved as local directives.

Runtime directives can only be used in the resources and scripts sections of the Stacktape configuration file.

  • For convenience, Stacktape resolves runtime directives locally (the value is automatically downloaded) when used inside a script or when using development commands (cw:run-local and fn:develop).

$ResourceParam()

Copy

resources:
myDatabase:
type: relational-database
properties: ...database properties...
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my/lambda-requiring-db.ts
environment:
- name: DB_CONNECTION_STRING
value: $ResourceParam('myDatabase', 'connectionString')

Example usage of $ResourceParam() directive.

$CfResourceParam()

  • Returns specified paremeter of a specified Cloudformation resource. For example name or port of the database, URL of the HTTP API Gateway, etc.
  • Arguments:
    • 1. cloudformation logical name - name of the AWS Cloudformation resource.
      • If you want to reference a resource defined in cloudformationResources section, just use its name.
      • If you want to reference a child resources of a Stacktape resource, you can get a list of child resource using stack-info command
    • 2. parameter name - parameter of the AWS Cloudformation resource to reference. To see a list of all referenceable cloudformation parameters, refer to Referenceable parameters docs
  • To learn more about referencing parameters of your infrastructure resources, refer to Referencing parameters.

Copy

cloudformationResources:
MySnsTopic:
Type: AWS::SNS::Topic
resources:
myBucket:
type: bucket
processData:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my/lambda.ts
destinations:
onFailure: $CfResourceParam('MySnsTopic', 'Arn')
environment:
- name: BUCKET_NAME
value: $CfResourceParam('MyBucketBucket', 'Name')

Example usage of $CfResourceParam() directive.

$Secret()

  • Returns the value of specified secret. To learn more about secrets, refer to secrets docs.
  • Arguments:
    • 1. secret name - Name of the secret. If the stored secret is in JSON format, it is possible to extract the nested properties using the dot notation.

Copy

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserName: admin
masterUserPassword: $Secret('my-master-pass')
...more properties...

$CfFormat()

  • Returns an interploated string.

  • Compared to $Format() directive, the $CfFormat() directive can contain directives resolved at runtime as arguments.

  • Arguments:

    • string to interpolate - Occurrences of {} will be replaced by latter arguments.
    • (arguments 2 to infinity) value to use in the interplation - The amount of arguments must be equal to the number of {} used in the first argument.
  • Examples:

    • Directive CfFormat('{}-{}', 'foo', 'bar') will result in foo-bar
    • Directive CfFormat('{}-mydomain.com', 'foo') will result in foo-mydomain.com
    • Directive CfFormat('{}.mydomain.com', $Stage()) will result in staging.mydomain.com given the stage is set to staging.

$CfStackOutput()

  • Returns the output of another stack. This allows you to reference resources deployed in another stack.

  • The stack you're referencing must be already deployed.

  • If you try to delete a stack that has a stack output referenced by another stack, you'll get an error.

  • If you want to get the output locally (download it and pass it as value), use the $StackOutput() directive.

  • Arguments:

    • stack name - name of the stack that contains the output. Note that if the stack was deployed using Stacktape, stackName has the following form: <<serviceName>>-<<stage>>.
    • output name - name of the output to reference.


Consider we have deployed stack base-stack with the following configuration to a dev stage.

Copy

serviceName: base-stack
stackConfig:
outputs:
- name: bucketName
value: $ResourceParam('baseBucket', 'arn')
resources:
baseBucket:
type: bucket

We can now reference output of the base-stack in another stack:

Copy

serviceName: service-stack
variables:
baseStackName: $Format('{}-{}', 'base-stack', $Stage())
resources:
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my/lambda-requiring-bucket-name.ts
environment:
- name: OTHER_STACK_BUCKET_NAME
value: $CfStackOutput($Var().baseStackName, 'bucketName')

Custom directives

  • User-defined directives.
  • Used to add custom logic (e.g. setting database instance size based on a stage or fetching data required in configuration).

Registering directives

  • To use a directive, you first need to register it in the directives section of your configuration file.
  • You can configure the function to use as your directive using the :. If you don't specify the function name,
    • for javascript and typescript directives: the default export is used
    • for python directives: the main function is used

Copy

directives:
- name: myDirective
filePath: path/to/my/directive.ts:functionName

DirectiveDefinition  API reference
name
Required
filePath
Required

Writing directives

  • Custom directives can be written in Javascript, Typescript or Python.
  • Custom directives can return primitive values, objects or arrays. If the returned value is an object, you can access it using the dot notation: $GetBooks().books.Dune.

In Javascript and Typescript

  • You can write directives using Javascript (ES2020) and Typescript. The code will be automatically transpiled.
  • Your directive can return a promise - it will be automatically awaited.
  • Any code OUTSIDE the function handler will be executed only once, when the configuration is parsed for the first time.

Copy

export const getDbInstanceSize = (stage: string) => {
if (stage === "production") {
return "db.m5.xlarge";
}
return "db.t2.micro";
};

Copy

directives:
- name: getDbInstanceSize
filePath: my-directive.ts:getDbInstanceSize
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserName: admin
masterUserPassword: $Secret('database.password')
engine:
type: postgres
properties:
dbName: mydatabase
port: 5432
primaryInstance:
instanceSize: $getDbInstanceSize($Stage())

In Python

  • Any code OUTSIDE the function handler will be executed only once, when the configuration is parsed for the first time.

Copy

def get_db_instance_size(stage):
if (stage == 'production'):
return 'M5'
return 'M2'

Copy

directives:
- name: get_db_instance_size
filePath: my-directive.py:get_db_instance_size
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserName: admin
masterUserPassword: $Secret('database.password')
engine:
type: postgres
properties:
dbName: mydatabase
port: 5432
primaryInstance:
instanceSize: $get_db_instance_size($Stage())

Need help? Ask a question on SlackDiscord or info@stacktape.com.