logoStacktape docs




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.

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

    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. To learn more about directive resultion, refer to Resolving directives
    • 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.

    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 subsituted with the value returned from the directive.

$CliArgs()

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

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.

myvar1=value1

resources:
myFunction:
type: function
properties:
packageConfig:
filePath: 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.

myvar1=value1

resources:
myFunction:
type: function
properties:
packageConfig:
filePath: 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.

{ "myvar1": "value1" }

resources:
myFunction:
type: function
properties:
packageConfig:
filePath: 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.

myvar1: value1

resources:
myFunction:
type: function
properties:
packageConfig:
filePath: 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.

variables:
eventName: myCustomEventName
resources:
# publishes events with EventName set to $Var().eventName into myEventBus
myPublisherFunction:
type: 'function'
properties:
packageConfig:
filePath: 'lambdas/event-bus-publisher.ts'
environment:
- name: EVENT_NAME
value: $Var().eventName
...
# listens for events with EventName set to $Var().eventName published into myEventBus
myConsumerFunction:
type: 'function'
properties:
packageConfig:
filePath: '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 configure-defaults command).

$Region()

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

$Profile()

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

$This()

  • Returns the current stacktape template as an object.

serviceName: my-service
resources:
myHttpApi:
type: 'http-api-gateway'
properties:
domainNames:
# 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

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.

serviceName: base-stack
stackConfig:
outputs:
- name: bucketName
value: $Param('baseBucket', 'Bucket::Name')
resources:
baseBucket:
type: bucket


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

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

Runtime directives

  • Some directives can not be resolved immediately, because the value is not yet known.
  • Example: $Param('myDatabase', 'DbInstance::Endpoint.Address') returns the endpoint of the myDatabase. The endpoint is only known after the myDatabase is created.
  • Runtime directives are used mostly to reference other infrastructure resources. For example to pass a database endpoint as an environment variable to your lambda function.
  • For convenience, when using invoke-local command, the results of runtime directives will be automatically downloaded and resolved as local directives.
  • Runtime directives can be used only in the resources section of your configuration.
  • For convenience, they can also be passed to environment variables of after:deploy hook.

$ResourceParam()

  • Returns specified paremeter of a specified Stacktape resource. For example name or port of the database, URL of the HTTP API Gateway, etc.
  • Arguments:
    • 1. stacktape resource name - name of the referenced Stacktape resource as specified in the configuration.
    • 2. parameter name - parameter to reference. You can find a list of all referenceable parameters for every resource in its docs:
  • To learn more about referencing parameters of your infrastructure resources, refer to Referencing parameters.
resources:
myDatabase:
type: relational-database
properties: ...database properties...
myFunction:
type: function
properties:
packageConfig:
filePath: path/to/my/lambda-requiring-db-endpoint.ts
environment:
- name: DB_ENDPOINT
value: $ResourceParam('myDatabase', 'endpoint')
- name: DB_PORT
value: $ResourceParam('myDatabase', 'port')

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.
cloudformationResources:
MySnsTopic:
type: AWS::SNS::Topic
resources:
myBucket:
type: bucket
processData:
type: function
properties:
packageConfig:
filePath: 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.
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.

serviceName: base-stack
stackConfig:
outputs:
- name: bucketName
value: $Param('baseBucket', 'Bucket::Name')
resources:
baseBucket:
type: bucket

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

serviceName: service-stack
variables:
baseStackName: $Format('{}-{}', 'base-stack', $Stage())
resources:
myFunction:
type: function
properties:
packageConfig:
filePath: 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

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

DirectiveDefinition  API reference
name
Required

Name of the directive

Name of the directive.

Type: string

filePath
Required

Path to the file where directive is defined. Format: {file-path}:{function-name}

Path to the file where directive is defined.

Type: string

  • Format: {file-path}:{handler}
  • If you do not specify {handler} part:
    • for .js and .ts files, default export is used
    • for .py files, main functions is used

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.
export const getDbInstanceSize = (stage: string) => {
if (stage === "production") {
return "db.m5.xlarge";
}
return "db.t3.micro";
};

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
instance:
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.
def get_db_instance_size(stage):
if (stage == 'production'):
return 'M5'
return 'M2'

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
instance:
instanceSize: $get_db_instance_size($Stage())