AWS Marketplace
CloudFormation Templates 101 for Sellers in AWS Marketplace
AWS Marketplace is a digital catalog that enables qualified Independent Software Vendors (ISVs) to market software solutions and makes it easy for AWS customers to find, buy, and deploy software on AWS.
In this post, I will explain a use case where CloudFormation template is a better fit than a Single-AMI solution, and then I will list some best practices to create an AWS CloudFormation template fit for AWS Marketplace. I will provide some related architectural considerations as well.
Single AMI to CloudFormation conversion
For an AMI-only solution, you need to provide exhaustive documentation for the manual steps needed to configure your product. By using CloudFormation templates, however, you can offer customers 1-click deployment, making your product easier to adopt.
I will take an example of a product that provides a high-performance computing platform and has the core of its solution baked into an AMI. Here’s what the setup for that product might look like:
- Create Identity Access Management (IAM) User and obtain Secret Key/Access Key from the console
- Create IAM policy document and attach to a the IAM User
- Create Amazon Elastic Compute Cloud (Amazon EC2) instance from product AMI and configure security groups
- Tag the instances—Master or Worker type
- Enter the Access Key and Secret Key as an input to a shell script, which takes care of the next level of configuration
- Run a script from Master to get the IPs of the worker nodes
- Using the results of the above script, setup password-less SSH connection between Master and Worker nodes
The steps mentioned above might be a feasible solution for a single EC2 instance, but not optimal for a cluster, primarily due to the manual steps involved. Manual processes like the one above inherently carry a risk of misconfiguration, and what if there is an error setting up a security group or while creating the IAM policy/role? In such cases, the customer would need to go back and repeat the steps again.
To avoid such errors, the manual steps above can be automated by running a CloudFormation template, providing customers a 1-click deployment experience. The reference architecture diagram below shows the same AMI-based product in a CloudFormation template packaged with the necessary AWS networking components—IAM Roles, Policy, AWS Auto Scaling, and Amazon Simple Storage Solution (Amazon S3) bucket.
Learn more about using AWS CloudFormation templates to deliver AMI-based products purchased in AWS Marketplace here.
10 CloudFormation Template Best Practices for solutions in AWS Marketplace
Here are some best practices to consider while creating a CloudFormation template for AWS Marketplace:
- It’s better to ask for VPC ID and Subnet ID as an input parameter and reference that for the resources like EC2 being created in your template. While your product might work in the default Amazon Virtual Private Cloud (Amazon VPC), some customers may choose to not deploy it there. Here’s an example on how to use VPC ID and Subnet ID as an input parameter. Below is a snippet of the Parameters section of a CloudFormation template.
"RefVPCId": { "Description": "VPC ID where EC2 instance is to be installed", "Type": "AWS::EC2::VPC::Id" }, "SubnetId": { "Description": "VPC Subnet used for EC2 instance", "Type": "AWS::EC2::Subnet::Id" },
And then in the Resources section of the template, we can reference these values as shown here:
"Resources" : {
"EC2Instance" : {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"InstanceType" : { "Ref" : "AWSInstanceType" },
"SecurityGroupIds" : [ { "Ref" : "InstanceSecurityGroup" } ],
"KeyName" : { "Ref" : "KeyName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -xe\n",
]]}},
"SubnetId": { "Ref": "SubnetId" },
"ImageId" : { "Fn::FindInMap" : [ "RegionMap", {"Ref" : "AWS::Region"} , "name"] }
}
},
- If your product needs new VPC/Subnets, I recommend you parameterize Classless Inter-Domain Routing (CIDR) blocks for VPC/Subnets and not hardcode them. Some customers may want to control their network periphery where the product is being deployed.
- To create a minimally-privileged IAM policy and IAM Roles instead of IAM Users, see AWS Security Best Practices. Here’s an example of an IAM policy for granting access to S3 Buckets/Objects.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["*"]
}
}
If your product just needs to read/write from a bucket, then the example above is an overtly permissive policy because it grants all S3 actions across all S3 buckets. Use this instead which has specific actions scoped to a particular Resource.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::test"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::test/*"]
}
]
}
- Create highly-available cloud architecture. As a best practice, create a highly-available architecture by having a Multi-Availability Zone (AZ), multi-subnet VPC infrastructure with managed Network Address Translation (NAT) gateways in the public subnet for each Availability Zone. You can also create additional private subnets with dedicated custom network access control lists (ACLs). Here are components that you’ll need to build out:
- VPC - public and private subnets
- Internet Gateway/VPC Gateway/NAT Gateway
- Route table and route table association
- Access control list
- Security groups
- Instead of using disparate EC2 instances, use an AWS Auto Scaling group to maintain a defined set of instances for your workload. Use an Amazon CloudWatch alarm and AWS Auto Scaling policy to define thresholds for scaling events. For example, add an instance when CPU is over 70% on the current instances for 15 minutes. It’s important to note that this will work only for stateless applications. For stateful applications, you’ll need to have multiple EC2 instances spread across different AZs to achieve high availability.
- If your product uses a default password for initial access, I recommend you randomize it. One of the popular patterns is to set it to the instance-id. It’s a 17-character random string and is set only during EC2 instantiation. You can retrieve it in UserData by querying instance metadata. End users can get this information from the AWS Management Console. Below is an example of an ISV offering a WordPress site backed by RDS (mysql) and the default wp-config.php.
define( 'DB_NAME', 'database_name_here' );
/** MySQL database username */
define( 'DB_USER', 'username_here' );
/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
Set these parameter values to specific passwords from UserData section. In this example, the Database name and UserName is hardcoded, but you the password has been set to instance-id:
"sudo sed -i -e \"s/'database_name_here'/'wpdemodb'/g\" /var/www/wordpress/wp-config.php\n",
"sudo sed -i -e \"s/'username_here'/'wpadmin'/g\" /var/www/wordpress/wp-config.php\n",
"sudo sed -i -e \"s/'password_here'/'`wget -q -O - http://169.254.169.254/latest/meta-data/instance-id`'/g\" /var/www/wordpress/wp-config.php\n
- The bootstrapping script in the UserData section of your EC2 instance should be such that an instance can automatically rejoin a cluster in a scaling event. This is important if you have a master-slave architecture. For example, you can expose the master node’s IP address as an Elastic IP address, and the slave nodes during bootstrap can query the master using that Elastic IP address. If the customer has to log in to the new instance and run configuration scripts to make it join a cluster, it negates the advantage of 1-click deployments.
- When delivering Bring Your Own License (BYOL) AMI’s in CloudFormation templates, you can ask for the license key file as an input parameter to your template and use that in the UserData section to activate the license during bootstrapping of the instance
- For allowing users to efficiently specify parameter values, you can make use of AWS::CloudFormation::Interface metadata key. By using this key, you can define your own parameter grouping and ordering and override CloudFormation console’s default behavior of listing parameters by alphabetical order of logical ID. For example, you could group all EC2-related parameters in one group and all VPC-related parameters in another group.
- Use Rules to enforce input parameters validation. For example, if you are asking for VPCId and SubnetId as input parameters you should validate that the subnet is in a valid virtual private cloud (VPC). The code snippet below does this validation. Here is a good blog post explaining how Rules work.
"Rules": {
"SubnetsInVPC": {
"Assertions": [
{
"Assert": {
"Fn::EachMemberIn": [
{
"Fn::ValueOfAll": [
"AWS::EC2::Subnet::Id",
"VpcId"
]
},
{
"Fn::RefAll": "AWS::EC2::VPC::Id"
}
]
},
"AssertDescription": "All subnets must exist in the VPC"
}
]
}
}
Other Architectural Considerations
- Use optimal instance types for the workload. For example, use the C4 and C5 instance families for compute-intensive workloads such as high-traffic websites or online gaming. For high-performing NoSQL databases such as Cassandra, use the I3 instance family.
- If your application can withstand jitters, consider Spot Fleet for significant cost savings. Spot Fleet can provide economical compute resources for stateless or task-based scenarios that run as long as they need and easily replaced with subsequent identical processes. Spot Fleet also attempts to replace any terminated instances to maintain the requested target capacity.
- Use placement groups if you need low-latency communication between instances.
- Use appropriate storage solutions for the workload with optimal performance settings. This whitepaper provides overview of the available storage options from AWS.
- Set up a password-less inter-node Secure Shell (SSH) connection between the cluster nodes automatically. Here is an example of such an implementation. In this example, we have used AWS Lambda to generate a dynamic SSH key pair that is then loaded into an Auto Scaling group.
AWS QuickStarts
AWS QuickStarts provide automated reference deployments for variety of products and implement these best practices. They have a GitHub repository of sample CloudFormation templates enabling ISVs and partners to adopt them easily. Follow some of the patterns here: https://github.com/aws-quickstart/quickstart-examples/.
Conclusion
In this post, I have shown how to use AWS CloudFormation templates to publish product offerings in AWS Marketplace, which enables 1-click deployment of those products. Using CloudFormation templates also reduces the number of manual steps for customers, especially for solutions involving multiple AWS Resources, and makes your products self-contained and easier for customers to adopt. I also provided best practices for using CloudFormation templates as well as architectural considerations for ISVs using templates to publish their products in AWS Marketplace.
About the author
Tapodipta Ghosh is a Solutions Architect at AWS. Tapo is passionate about DevOps methodologies and loves to innovate to solve customer problems. Outside of work, he loves to spend time with his four-year-old daughter. In his spare time, you will find him listening to music, playing chess, watching soccer, or reading technical blogs.