AWS Cloud Operations Blog

How to perform cross-parameter validation using AWS CloudFormation rules and assertions

Most AWS CloudFormation templates use parameters to enable customization. It’s important to validate parameters to ensure a good user experience. AWS CloudFormation gives you several ways to perform parameter validation. For example, you can specify AllowedValues or a Default, or you can assign Types. For more information on validating parameters, see the documentation.

Often, developers run into situations where a parameter value depends on another parameter. For example, when you create an Application Load Balancer, a certificate is required only for a secure listener (HTTPS), and it is not required for the default listener (HTTP). So, if CloudFormation developers have a common template that can conditionally create the listener, they would need to have two parameters: 1) Choose ‘Yes’ if you want to create a secure Application Load Balancer. 2) If yes, enter the Amazon Resource Name (ARN) of the certificate. Here, the ARN of the certificate is only required if the response for the first parameter is ‘Yes.’ How do we enforce this validation to ensure that the user enters the input correctly?

The situation I just described is an example of cross-parameter or cross-field validation. AWS CloudFormation uses rules to validate parameter values before it creates the resources for the product.For example, you can add a rule to ensure that end users have specified a public subnet for a NAT gateway or have used smaller instance types for Dev/Test environments. In this blog post, we look at how AWS CloudFormation rules and assertions can be used to perform validations such as these.

The Rules section is a separate section in a CloudFormation template. There are two properties of a rule: 1) A rule condition, which is an optional property, and 2) An assertion, which is mandatory. To apply the assertion conditionally, a rule condition is used. In other words, an assertion is applied only when a condition evaluates to true. Otherwise, the assertion is ignored. If the condition construct is not specified in the rule, the assertion is always applied. Rule-specific intrinsic functions can be used to define rule conditions and assertions. In addition, the error message that you want to display when the assertion returns false is given as part of the AssertDescription property. To learn more about the structure and syntax of rules, see the documentation.

Rules can be used to perform parameter validations based on the values of other parameters. For example, consider the case where you want to make sure that all the subnets selected in a template are within the same virtual private cloud (VPC). This doesn’t require a condition to be a part of the rule. The assertion is evaluated based on the value of the subnet’s VPC ID, and the CloudFormation stack operation aborts if it’s false. Here’s the code snippet that performs this validation.

{
    "Rules": {
        "SubnetsInVPC": {
            "Assertions": [
                {
                    "Assert": {
                        "Fn::EachMemberIn": [
                            {
                                "Fn::ValueOfAll": [
                                    "AWS::EC2::Subnet::Id",
                                    "VpcId"
                                ]
                            },
                            {
                                "Fn::RefAll": "AWS::EC2::VPC::Id"
                            }
                        ]
                    },
                    "AssertDescription": "All subnets must in the VPC"
                }
            ]
        }
    }
}

Assertions can be applied conditionally. For example, if you want to enforce that users must provide an ACM certificate as well as a hosted zone if they configure an SSL listener on an Application Load Balancer, you can use the condition property of the rule. You can also group several assertions together if they are based on the same condition. For example, consider the following snippet. Since both the ACM certificate and the hosted zone name are required only if a secure listener is chosen, they are grouped under the same condition “UseSSL”.

{
    "Rules": {
        "ValidateHostedZone": {
            "RuleCondition": {
                "Fn::Equals": [
                    {
                        "Ref": "UseSSL"
                    },
                    "Yes"
                ]
            },
            "Assertions": [
                {
                    "Assert": {
                        "Fn::Not": [
                            {
                                "Fn::Equals": [
                                    {
                                        "Ref": "ALBSSLCertificateARN"
                                    },
                                    ""
                                ]
                            }
                        ]
                    },
                    "AssertDescription": "ACM Certificate value cannot be empty if SSL is required"
                },
                {
                    "Assert": {
                        "Fn::Not": [
                            {
                                "Fn::Equals": [
                                    {
                                        "Ref": "HostedZoneName"
                                    },
                                    ""
                                ]
                            }
                        ]
                    },
                    "AssertDescription": "Route53 Hosted Zone Name is mandatory when SSL is required"
                }
            ]
        }
    }
}

Note that only those conditions that are specified within the Rules block can be applied on the assertions. Those conditions that are outside the Rules block (that is, in the Conditions block) can’t be used. Also note that only specific intrinsic functions are available for use within the Rules. For more information, here is a list of those functions and a link to the complete template where the code snippets mentioned previously are used. This template creates a load balanced, auto scaled sample website where the user can optionally configure an HTTPS/SSL listener.

Conclusion

By using CloudFormation rules and assertions infrastructure-as-code developers can create sophisticated validation logic and avoid errors and issues when executing stack operations. This ultimately saves troubleshooting time for your template and stack users.

About the author

Madhuri Susarla is a solution architect in the partner team of AWS. She is passionate about working with large Global System Integrators to create value in the era of cloud computing. She dabbled in multiple cloud platforms. She believes in enabling reusability by way of content creation and public speaking. She enjoys playing with her 3 boys while struggling to keep up with their soccer knowledge and gastronomic choices.