Serverless & Terraform for your “functions” CI
Serverless is a framework allowing you to deploy “functions” on various different cloud providers such as AWS Lambda or Google Cloud Functions in an easy & automated manner. Terraform allows you to automate the deployment of cloud-specific configurations such as AWS SNS topics, SQS queues, IAM roles, etc…
Terraform output
This article won’t go into the details of Terraform, you can read more in the documentation. The key point is that when “running” your Terraform, you will be able to return values: they are called outputs.
You can get the output variables as a JSON object by running this command:
$ terraform output -json
Now, let’s see how we can use this within Serverless.
Serverless `custom` configuration
Within your serverless.yml
configuration, you can set custom variables. Using the file(...)
processor, you can also run a JavaScript file.
Hint: what if such JavaScript file was reading the output of the terraform output -json
command?
# serverless.yaml
# ...provider:
# ...
environment:
FOO: ${self:custom.fromTerraform.foo_value}custom:
fromTerraform: ${file(./terraform-outputs-as-variables.js)}
In the example above, an environment variable FOO
will be available to all your functions. The value will be the foo_value
variable returned by our JavaScript file. So how is this file looking like?
The JavaScript glue
This JavaScript file will be responsible for running the terraform output command, parsing it and returning the values in a format understandable by Serverless.
This file above will, based on the environment variable SERVERLESS_ENVIRONMENT
, run the command in the production
or staging
folder, containing your different Terraform configurations.
We use it for more than environment variables
The serverless.yml
example above is showing how to use such value coming from terraform output
as environment variables. Indeed, you can use it for much more. We are, for example, using it for VPCs and Lambda IAM roles:
# serverless.yml
# ...provider:
# ...
vpc:
securityGroupIds:
- ${self:custom.fromTerraform.security_group_id}
subnetIds:
"Fn::Split":
- ","
- ${self:custom.fromTerraform.subnet_ids}functions:
# ...
foo:
handler: path/to/function.handler
role: ${self:custom.fromTerraform.tunstall_dump_lamba_role_arn}
events:
- sns: ${self:custom.fromTerraform.tunstall_ingress_sns_topic_arn}
onError: ${self:custom.fromTerraform.sns_dlq_tunstall_dump_arn}
Enjoy! But don’t rush to functions, think about your monolith ;-)