In the past couple of months, I’ve been working on a project that allows deploying across clouds and represents a tool for monitoring and security of environments. Getting insight and monitoring the resources of infrastructure has always been one of our biggest challenges while working and deploying an application.
To gain cloud access, we wrote a CloudFormation template which creates an IAM role that companies should use in their AWS account. But we wanted this approach to be more automatic. So, in order not to wait for the template to be created (we know this could take some time), we decided to use the Custom AWS resources. In this way, we do not need the template to be finished to gain the necessary outputs. Our custom provisioning logic will do that for us.
During the stack operation, the custom resource sends a request to a service token that is specified in our template and then waits for a response before proceeding. The service token could be the ARN of an AWS SNS topic or an AWS Lambda function. We created a custom payload with the Properties field to send to our SNS topic, which is a trigger for a Lambda function.
Part of the CloudFormation template:
The usage of the cfn-response module in our Lambda code is inevitable since we need to send the result back to the invoker custom resource. This is important in order to finish creating or deleting of the CloudFormation stack successfully.
In this way we receive data which under the ‘ResourceProperties’ field will contain information of the created CloudFormation stack. In our situation, we need the Role ARN as an output – so we can use temporary credentials to gain insight to an AWS account and some of the AWS resources that we have specified in the template.
You might notice that we built the service token with a reference to:
- The region in which the stack is created
- Our AWS Account Id
- Our SNS Topic Name
The custom resources use the service token and serve as invokers. The resource behind this token could be placed in any AWS account, but it must be in the same region in which we are creating the stack. So here comes the fun part, thus we do not know for which region the customer will give us access and create the stack. We have had the opportunity to deploy TyphoonX infrastructure across multiple regions, so in this way, no matter where the template is created – we will be notified and we will use the cfn-response ‘send’ method to send the result back. We use an SNS topic deployed in every region, to handle the stack creation and receive a message of its success – or failure – most directly. We easily did a multi-region deployment using Terraform modules.
A module is a container for multiple resources that are used together. – Terraform docs
While working with modules, we can create a reusable infrastructure, instead of having to copy-paste our code for deploying in every region and every environment. This would save quite some time, hence we need to deploy resources in multiple regions that are basically the same.
Our provider.tf looks like this:
Our module.tf for deploying in two regions looks like this:
As you can see, in each module we say where is the source and inject the provider into the module (the AWS region, referred to as the ‘alias’). We provide our lambda function for the cloud access creation as terraform data source. This lambda is deployed in one region only (in the root folder, where we use our default region eu-west-1), which serves as the destination that will be invoked by the resources created from each module (in their region).
We create a folder called modules, which contains a simple sns.tf file:
As you can see, the name of the topic is the same in every region and is passed as a parameter in the CloudFormation template. That way, the service token is a joined string containing our AWS AccountId, this name, and the region.
In a different case, we could pass the name of the topic as a variable, so it differs. We define a variable “lambda_arn” to create a topic subscription. We pass it from the module definition – along with the “env” variable – to the root folder. We will expose the created SNS topic ARN as an output, hence we need that in the root folder (not in the modules folder) to create an AWS Lambda Permission for each SNS, deployed from its module. For example, when an SNS topic from the module ‘eu-west-2’ is created, we are ready to use its ARN under the name “module.eu-west-2.sns_arn”.
Here we use terraform syntax for_each and local values in favour of creating a map of the exposed module outputs, so we can use the key to create a unique statment_id and the value for the source_arn in the ‘aws_lambda_permission’ resource.
This is a practical use-case that we usually come across when we need to do a creation of a CloudFormation stack in one account and receive a notification on another AWS account, regardless of the region. Thus, with terraform we were resilient enough to deploy our infrastructure needed for this scenario in multiple environments, and of course, regions.
I work on TyphoonX – a project by Alite – which is a cloud-native platform that automates application lifecycle management in cloud environments, focusing on AWS.