AWS CloudFormation Custom Resource No Longer Required for Spotinst

“Infrastructure as Code” also known as IaC has become very popular for deploying and managing infrastructure on the cloud. For AWS customers, Cloudformation or CFN is the go-to IaC of choice. CFN makes it easy to standardize infra components for a single point of truth, better compliance and easier troubleshooting across any organization. 

When it comes to deployment, CFN provisions resources without any need for manual actions or custom scripts, with CFN handling all aspects of the infrastructure management including automated rollbacks if any problems occur. 

All that needs to be done by the users, is code the desired infrastructure components with CFN’s template language, either in YAML or JSON format. Then they can either use CFN’s UI, command-line tools or APIs to access their template (either on a local machine or in S3) to create the desired infra stack. 

CFN then goes ahead and provisions what was specified in the template and only at that point is there any cost to the user, as AWS begins to charge for the deployed AWS components. 

AWS CloudFormation Registry – No More Need for Custom Resources for Spotinst

Until recently, CFN would directly provision only AWS services and components. For 3rd party software, CFN users would need to create a Custom Resource that CFN would interact with. 

For Spotinst, we had in the past created a Lambda function which after receiving instructions from a CFN  stack would call the Spotinst API to take the necessary actions on Spotinst Elastigroup resources. 

Going forward, Amazon provides its customers with their new AWS CloudFormation Registry which will have 3rd party resources available to connect with and directly invoke those resources.  This will greatly simplify the process of deploying infrastructure with CFN and Spotinst.

Getting Started

Get Started with AWS CloudFormation Registry and Spotinst (Step-by-Step Guide)

With the introduction of custom 3rd party resources, Spotinst released its own custom resources. Once it is installed, you can start using the new CFN templates while referring to Spotinst custom resources.

1. Registering Spotinst as a Resource

To install the Spotinst custom resource, download the provider .zip file from the following URL:
https://spotinst-public.s3.amazonaws.com/integrations/cloudFormation/registry/spotinst-elastigroup-group.zip

Next, upload the provider to an S3 bucket accessible via your AWS CLI, and note the S3 path. Then, run the following command on an AWS CLI enabled terminal:

aws cloudformation register-type \
--region us-west-2 \
--type-name "Spotinst::Elastigroup::Group" \
--schema-handler-package <LINK_TO_S3> \
--type RESOURCE

The above command will output a registration-token. Now, using the token, you can sample the status of the registration process with the following command:

aws cloudformation describe-type-registration \
--region us-east-1 \
--registration-token <TOKEN>

At first, we see that the registration is IN_PROGRESS

After a couple of minutes, describing the registration again should indicate completion via the following output:

Once the process is completed you will find the new resources in your AWS Cloudformation console, under the new “CloudFormation registry” section:

 

2. Create a Resource

To create an Elastigroup, you may use the following CloudFormation template example as a starting point:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Spotinst elastigroup provider test",
  "Resources": {
    "Elastigroup": {
      "Type": "Spotinst::Elastigroup::Group",
      "Properties": {
        "credentials": {
          "accessToken": "SPOTINST_API_TOKEN",
          "accountId": "act-12345678"
        },
        "group": {
          "name": "ElastigroupWithEcs",
          "region": "us-west-2",
          "capacity": {
            "minimum": 1,
            "maximum": 3,
            "target": 1,
            "unit": "instance"
          },
          "strategy": {
            "risk": 100,
            "availabilityVsCost": "balanced",
            "drainingTimeout": 60,
            "lifetimePeriod": "days",
            "fallbackToOd": true,
            "revertToSpot": {
              "performAt": "always"
            }
          },
          "compute": {
            "instanceTypes": {
              "ondemand": "m4.xlarge",
              "spot": [
                "r4.2xlarge",
                "c5.xlarge",
                "c5.4xlarge",
                "m5.4xlarge"
              ]
            },
            "availabilityZones": [
              {
                "name": "us-west-2a",
                "subnetIds": [
                  "subnet-79da021e"
                ]
              },
              {
                "name": "us-west-2b",
                "subnetIds": [
                  "subnet-0cb5a07e1df98b3fd"
                ]
              }
            ],
            "product": "Linux/UNIX",
            "launchSpecification": {
              "healthCheckType": "ECS_CLUSTER_INSTANCE",
              "healthCheckGracePeriod": 300,
              "securityGroupIds": [
                "sg-0412e52ce42b06efc"
              ],
              "monitoring": false,
              "ebsOptimized": false,
              "imageId": "ami-00d4f478",
              "iamRole": {
                "arn": "arn:aws:iam::842422002533:instance-profile/ec2-instance-full-access"
              },
              "userData": "SXlFdlltbHVMMkpoYzJnZ0RRb2daV05vYnlCRlExTmZRMHhWVTFSRlVqMTBaWE4wTFd0aGNpQStQaUF2WlhSakwyVmpjeTlsWTNNdVkyOXVabWxuTzJWamFHOGdSVU5UWDBKQlEwdEZUa1JmU0U5VFZEMGdQajRnTDJWMFl5OWxZM012WldOekxtTnZibVpwWnc9PQ==",
              "blockDeviceMappings": [
                {
                  "deviceName": "/dev/sda1",
                  "ebs": {
                    "deleteOnTermination": true,
                    "volumeSize": 20,
                    "volumeType": "standard"
                  }
                }
              ]
            }
          },
          "thirdPartiesIntegration": {
            "ecs": {
              "clusterName": "test-kar",
              "autoScale": {
                "isEnabled": true,
                "cooldown": 300,
                "isAutoConfig": true,
                "shouldScaleDownNonServiceTasks": false
              }
            }
          }
        }
      }
    }
  }
}

Note that the “group” object is a standard Elastigroup JSON. The above example shows an Elastigroup integrated with an ECS cluster. You may refer to the Spotinst Elastigroup API for more details.

Once you have the template ready, you can save it locally and apply via the CloudFormation console or the following command:

aws cloudformation create-stack \
--region us-west-2 \
--template-body "file://stack.json" \
--stack-name spotinst-ecs2

And that’s it! 

Open Sourced and Extensible

Note that upon release the Spotinst Cloudformation provider supports only the Elastigroup resource. The Spotinst provider files are open source so go ahead and check out the github repository, and if you have any questions, feel free to reach out!