Validation is a part of the AWS CDK app lifecycle described in the official documentation under the CDK app synthesis section:
All constructs that have implemented the validate method can validate themselves to ensure that they're in a state that will correctly deploy. You will get notified of any validation failures that happen during this phase. Generally, we recommend performing validation as soon as possible (usually as soon as you get some input) and throwing exceptions as early as possible. Performing validation early improves reliability as stack traces will be more accurate and ensures that your code can continue to execute safely.
While there are good articles describing how to handle AWS CDK validation in TypeScript linked below, I couldn't find a corresponding example in Python. After creating a few validations in Python, the following examples complement the ones in the articles.
The following examples we developed using AWS CDK v2.176.0 and Python 3.13.1.
Example Validation
The implementation of the validation is done using the validate method that returns a list of error messages. If the list is empty, there are no errors, and multiple entries correspond to the different validation checks that failed for this validation. The validation logic is encapsulated in a class annotated with @jsii.implements(IValidation)
, indicating that it is the correct type to be used for the validation. In this particular case, there is a check that the Amazon S3 bucket passed to the validation uses a custom AWS KMS key, giving more control over the encryption process compared to the default encryption used by Amazon S3.
@jsii.implements(IValidation)
class KodlotS3KmsValidator:
"""Checking that the S3 bucket is encrypted with a custom KMS key."""
def __init__(self, s3_bucket: s3.Bucket):
self.s3_bucket = s3_bucket
def validate(self) -> List[str]:
error_messages: List[str] = []
if self.s3_bucket.encryption_key is None:
error_messages.append(f"Bucket must be encrypted with a custom KMS key")
return error_messages
AWS CDK Construct Example
This is a basic example of an AWS CDK construct adding a single validation using the add_validation
method of the construct node in the scope tree. There was a mistake made while implementing, where the custom AWS KMS key was created but not passed to the encryption_key
parameter of the Amazon S3 Bucket
construct.
class KodlotS3Bucket(Construct):
"""S3 bucket with validations."""
def __init__(self, scope: Construct, construct_id: str, bucket_name: str) -> None:
super().__init__(scope, construct_id)
s3_kms_key = kms.Key(self, f"KodlotS3KmsKey{bucket_name.title()}")
self.s3_bucket = s3.Bucket(
self,
f"KodlotS3{bucket_name.title()}",
bucket_name=bucket_name,
encryption_key=None,
)
self.node.add_validation(KodlotS3KmsValidator(self.s3_bucket))
This type of mistake could easily be overlooked, and that's where the validation comes in help. Running CDK synthesis or deployment with this example results in the following error:
RuntimeError: Error: Validation failed with the following errors:
[KodlotS3Stack/KodlotBucket1] Bucket must be encrypted with a custom KMS key
Multiple Validations
There can be multiple validators testing different aspects of our configurations, and they can be reused for different constructs if we find such common patterns. In this example, a validation check if the Amazon S3 bucket was not accidentally configured for static website hosting is introduced, which, in this scenario, would be a mistake.
@jsii.implements(IValidation)
class KodlotS3NotWebsiteValidator:
"""Checking that the S3 bucket is NOT configured for static website hosting."""
def __init__(self, s3_bucket: s3.Bucket):
self.s3_bucket = s3_bucket
def validate(self) -> List[str]:
error_messages: List[str] = []
if self.s3_bucket.is_website:
error_messages.append(f"Bucket must not be used for static website hosting")
return error_messagesAWS CDK Construct Example Updated
AWS CDK Construct Example Updated
To use the additional validator checking that no website hosting is configured for the Amazon S3 bucket, the example CDK construct is updated, as shown below. This time, the original bug of not passing the custom AWS KMS key is kept. And now, additionally, there is a misconfiguration of passing website_index_document
to the Amazon S3 Bucket
construct, indicating that it will be used for static website hosting.
class KodlotS3Bucket(Construct):
"""S3 bucket with validations."""
def __init__(self, scope: Construct, construct_id: str, bucket_name: str) -> None:
super().__init__(scope, construct_id)
s3_kms_key = kms.Key(self, f"KodlotS3KmsKey{bucket_name.title()}")
self.s3_bucket = s3.Bucket(
self,
f"KodlotS3{bucket_name.title()}",
bucket_name=bucket_name,
encryption_key=None,
website_index_document="index.html",
)
self.node.add_validation(KodlotS3KmsValidator(self.s3_bucket))
self.node.add_validation(KodlotS3NotWebsiteValidator(self.s3_bucket))
This time, there are two different issues with our Amazon S3 bucket, and as we can see below, both are reported by the validation mechanism. Running CDK synthesis or deployment with this example results in the following errors:
RuntimeError: Error: Validation failed with the following errors:
[KodlotS3Stack/KodlotBucket1] Bucket must be encrypted with a custom KMS key
[KodlotS3Stack/KodlotBucket1] Bucket must not be used for static website hosting
This is very powerful, as we can collect the different configuration issues across our cloud infrastructure and address them all without needing to rerun and fix them one by one.
AWS CDK Stack Validation
It is worth remembering that validations are not reserved for AWS CDK constructs and can also be added at an AWS CDK stack level. Here, we can f.e. check if a given stack does not contain unwanted resources. If we have a requirement that our stack should not contain any AWS Lambda resources, then we can model that requirement using the validation presented below. Using the node construct's children, it is possible to check if any resource is an instance of AWS CDK Function
using Python's built-in isinstance
function.
@jsii.implements(IValidation)
class KodlotNoLambdaInStackValidator:
"""Checking that NO Lambda function was defined in a given stack."""
def __init__(self, stack: cdk.Stack):
self.stack = stack
def validate(self) -> List[str]:
error_messages: List[str] = []
for construct in self.stack.node.children:
if isinstance(construct, _lambda.Function):
error_messages.append(f"Stack must not contain any Lambda functions")
return error_messages
AWS CDK Stack Example
To add the validation for the stack, we use the same add_validation
method as for the constructs. Since KodlotS3Stack
was identified as a stack that must not contain any AWS Lambda functions, it is a good place to test the example stack validation. If a mistake is made during development, such as the one presented below, where the developer defines the AWS Lambda function resource, the validation will take effect.
class KodlotS3Stack(cdk.Stack):
"""Stack containing S3 buckets."""
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
KodlotS3Bucket(self, "KodlotBucket1", bucket_name="kodlot1")
inline_code = """
def handler(event, context):
return 200
"""
_lambda.Function(
self,
id="MyLambda",
runtime=_lambda.Runtime.PYTHON_3_13,
handler="index.handler",
code=_lambda.Code.from_inline(inline_code),
)
self.node.add_validation(KodlotNoLambdaInStackValidator(self))
Running CDK synthesis or deployment with this example results in the following error:
RuntimeError: Error: Validation failed with the following errors:
[KodlotS3Stack] Stack must not contain any Lambda functions
Built-in validations
It is also worth checking if AWS CDK does already provide checks for some aspects of our cloud infrastructure. In the context of Amazon S3, as it turns out, if we configure the bucket name of k1
, resulting in an illegal name length, there will be the following build-in error:
RuntimeError: Invalid S3 bucket name (value: k1)
Bucket name must be at least 3 and no more than 63 characters
Similarly, if we configure the bucket name of k_1
, resulting in an illegal underscore character present in it, there will be the following build-in error:
RuntimeError: Invalid S3 bucket name (value: k_1)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 3)
Conclusions
AWS CDK validation is a helpful mechanism for developers to use in conjunction with unit and integration tests to ensure the overall quality of the cloud computing infrastructure. Having the ability to capture common misconfigurations and define standards will result in reusable validations that can be shared across the different components. Validations can be added on different levels of AWS CDK definitions, both for AWS CDK constructs and AWS CDK stacks. It is good to see that AWS CDK is capturing some common validations already, as they are foundational to the various cloud services and resources and should always be checked, thus reducing the chance of facing deployment errors in production.
Top comments (0)