Cloudformation is very useful for creating and deploying AWS resources such as EC2 instances, S3 buckets, and ECS clusters. It not only allows you to keep your infrastructure as code, but it also keeps track of all of the resources together as a logical unit known as a Stack. It goes like this.
- Write Template File (YAML) containing AWS resources
- Test the deployment with Taskcat by doing a dry run
- Your resources are deployed together as a single unit known as a Stack.
The benefit of Stacks is that all of your resources are created at the same time, but also they will also be deleted at the same time when you inevitably must shut down your application. Instead of removing each resource one by one (deleting the EC2 instance, deleting the S3 bucket, deleting the security group, etc.) you run a single command
aws cloudformation delete-stack --stack-name test-stack and everything is deleted at the same time.
A Cloudformation template has the following main sections
- AWSTemplateFormatVersion: Metadata informing Cloudformation about what version of cloudformation template file it can expect to see.
- Description: A description of the template file
- Parameters: These are basically variables that hold values that can be substituted later in the template
- Resources: This is where we define AWS resources like EC2 instances, S3 buckets, ECS Clusters, etc.
There is also an Outputs section you can use to capture output from created resources (ARNs, IDs, created Hostnames/URLs, etc.), but for the purposes of this template file, it is not necessary.
The resource we are creating is an AWS::ECS::Cluster type. It uses a default capacity provider strategy. Basically, any deployment made to it will be evenly split between FARGATE and FARGATE_SPOT instance types. That is what the weights mean. For every instance deployed via the FARGATE capacity provider, one should also be deployed via the FARGATE_SPOT capacity provider (1:1 ratio). If you want to only use FARGATE_SPOT for example, you can set the FARGATE weight to zero. For more information on CapacityProviderStrategy and weights, I invite you to read the following documentation.
When you are using FARGATE_SPOT, you are not using dedicated hardware, but using cluster resources as they are made available to you. This turns out to be cheaper, although less reliable because your spot instances can be taken from you at any time. Although you can design around this using Fault-Tolerant, or parallelizable workloads.
esting The Template File with Taskcat
Taskcat is a very useful tool for doing a dry run of your stack before actually deploying. It is important to test your Template files because some resources need to be created before others (e.g. an ECS Service Definition that depends upon a load balancer to be available). Also, some resources or AWS services may not be available in all regions. Taskcat will catch these errors and generate a report.
You can install Taskcat with pip. It is also a good idea to install the cloudformation linter
cfn-lint as well. This can be used to inform you if you have any syntax errors in your template file.
pip install taskcat cfn-lint
Taskcat expects your directory structure to be like the following
Note: your .taskcat.yml file must have the extension .yml and not .yaml.
The contents of
project: name: ecs regions: - us-east-2 tests: default: template: FargateCluster.yaml parameters: ECSClusterName: "test-cluster"
The project name must be the same as the project directory, in this case ecs, which must also be all lower case (taskcat is very picky). We specify which regions we want to test in the project regions section. It is important to note that the stacks we are creating are region-specific. The rest is pretty straightforward.
Before we do our test run, we should lint the template file to make sure it is free of syntax errors which would prevent it from running. You can either use
cfn-lint or taskcat, both do the same thing (although, I’m pretty sure taskcat just uses
cfn-lint so you’re better off just using that).
Next, we’ll do a dry run with
taskcat test run This causes taskcat to create a temporary stack using the Template file provided in the specified regions and with the passed parameters. Afterward, it will delete the stack and report CREATE_COMPLETE if the stack was created successfully, or CREATE_FAILED if the stack failed along with any errors indicating the cause.
Taskcat will generate an HTML file in the project directory. For example
Once the stack has passed the dry run, we can move on to deployment.
Creating the Cloudformation Stack
Deploying the stack can be done through the AWS CLI.
aws cloudformation create-stack --region us-east-2 --stack-name test-stack --template-body file://FargateCluster.yaml
You can list your created stacks
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE
If your stack is deployed, you should be able to see your Fargate cluster in the AWS console.
Lastly, if you want to delete your test stack, which will delete the cluster.
aws cloudformation delete-stack --region us-east-2 --stack-name test-stack
One Last Thing
Okay, there is one more thing that I should mention. It is a slight nuisance on the part of taskcat. Every time you run taskcat, it creates an S3 bucket and uploads all Template files in the project directory to this bucket. It needs to do this to be able to create the stack.
The problem is, once you start editing larger Template files, you will run taskcat a lot, and you will get a bucket for each version of the template file you ran. It’s very annoying. So if you want to just delete all of these buckets, here is a small script that does just that.