<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Krzysztof Slowinski</title>
    <description>The latest articles on DEV Community by Krzysztof Slowinski (@krzyslow).</description>
    <link>https://dev.to/krzyslow</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2735805%2F11967afb-e8a8-4035-ae2b-14719b2db778.jpg</url>
      <title>DEV Community: Krzysztof Slowinski</title>
      <link>https://dev.to/krzyslow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/krzyslow"/>
    <language>en</language>
    <item>
      <title>1 million Amazon S3 buckets with AWS CDK</title>
      <dc:creator>Krzysztof Slowinski</dc:creator>
      <pubDate>Tue, 11 Feb 2025 08:51:41 +0000</pubDate>
      <link>https://dev.to/krzyslow/1-million-amazon-s3-buckets-with-aws-cdk-2469</link>
      <guid>https://dev.to/krzyslow/1-million-amazon-s3-buckets-with-aws-cdk-2469</guid>
      <description>&lt;p&gt;With &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-up-1-million-buckets-per-aws-account/" rel="noopener noreferrer"&gt;this recent announcement from AWS&lt;/a&gt; it is now possible to create up to 1 million &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; buckets in a single AWS account. Let's look at how it can be achieved using &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt; infrastructure as code tool.&lt;/p&gt;

&lt;p&gt;Disclaimer: per every additional Amazon S3 bucket above 2,000, there is currently a $0.02 monthly fee (details under &lt;a href="https://aws.amazon.com/s3/pricing/" rel="noopener noreferrer"&gt;Security &amp;amp; buckets&lt;/a&gt;), which will result in a &lt;strong&gt;$19,960&lt;/strong&gt; monthly fee if 1 million Amazon S3 buckets are created in a single AWS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;The amount of data created yearly is &lt;a href="https://www.statista.com/statistics/871513/worldwide-data-created/" rel="noopener noreferrer"&gt;accelerating at a rapid pace&lt;/a&gt;, with the prediction of &lt;a href="https://www.statista.com/chart/17727/global-data-creation-forecasts/" rel="noopener noreferrer"&gt;exponential growth between 2025 and 2035&lt;/a&gt;. This is reflected by &lt;a href="https://www.statista.com/outlook/tmo/data-center/storage/worldwide" rel="noopener noreferrer"&gt;the revenue generated by the storage market worldwide&lt;/a&gt;. Data storage systems need to adapt to that scale. Cloud computing infrastructure with scalability at its core is an alternative to consider when designing systems for the future.&lt;/p&gt;

&lt;p&gt;In a scenario where storage needs are growing, logically grouping the data according to its type or sources could be an additional requirement. Implementing this requirement in AWS can be achieved by keeping each category in a distinct Amazon S3 bucket. An example is a data lake platform, where the architects have decided to place each dataset in a separate Amazon S3 bucket for cost and governance reasons. Thousands of Amazon S3 buckets could be created for large organisations that generate many datasets daily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;For solutions of this scale, examining organisational best practices is essential, as highlighted in &lt;a href="https://aws.amazon.com/blogs/devops/best-practices-for-scaling-aws-cdk-adoption-within-your-organization/" rel="noopener noreferrer"&gt;this article&lt;/a&gt;. A dedicated platform engineering team could be assigned to concentrate on architecting and implementing complex solutions like the one presented here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Architecture
&lt;/h3&gt;

&lt;p&gt;The platform engineering team might start by placing all Amazon S3 buckets in top-level &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/stacks.html" rel="noopener noreferrer"&gt;AWS CDK stacks&lt;/a&gt;. It's a good start. However, the team quickly realises there is an issue with this approach. AWS CDK uses &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html" rel="noopener noreferrer"&gt;AWS CloudFormation&lt;/a&gt; for the deployment. AWS CDK stacks correspond to the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/stacks.html" rel="noopener noreferrer"&gt;CloudFormation stacks&lt;/a&gt;. The team discovers a current limit of 500 resources that can be placed in a single AWS CloudFormation stack (details under &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html" rel="noopener noreferrer"&gt;Resources&lt;/a&gt;). This is a hard limit, and it can't be increased by a request to AWS. Aiming to create 1 million resources will result in placing them into 2,000 AWS CloudFormation stacks, each composed of 500 resources with the &lt;code&gt;KodlotS3Stack&lt;/code&gt; example below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6o6zgbg288udasp5iqu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6o6zgbg288udasp5iqu2.png" alt="Top-level AWS CDK Stack Design" width="800" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It turns out that the 2,000 AWS CloudFormation stacks that the team needs to create with this solution is exactly the limit on the number of stacks that can be created in a single AWS account (details under &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html" rel="noopener noreferrer"&gt;Stack&lt;/a&gt;). In this case, it is a soft limit, and it can be increased by a request to AWS. Still, if the AWS account is to be used for purposes other than storage, creating 2,000 top-level stacks can make finding other stacks difficult. The team starts to explore what alternative options they have to refine the initial architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refinement 1 - Using Nested Stacks
&lt;/h3&gt;

&lt;p&gt;With the concept of &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html" rel="noopener noreferrer"&gt;AWS CloudFormation nested stacks&lt;/a&gt; (and the corresponding &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/stacks.html#stacks-work" rel="noopener noreferrer"&gt;AWS CDK nested stacks&lt;/a&gt;), the top-level stack can contain up to 500 nested stacks in it. Each AWS CloudFormation nested stack can contain up to 500 resources, as presented in the &lt;code&gt;KodlotS3NestedStack&lt;/code&gt; example below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxi9h90ypwveotnp7eao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxi9h90ypwveotnp7eao.png" alt="Nested AWS CDK Stack Design" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the introduction of the &lt;code&gt;KodlotS3NestedStack&lt;/code&gt; definition, the original &lt;code&gt;KodlotS3Stack&lt;/code&gt; can now be defined as presented below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzthrhdh4snmyo72lc5uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzthrhdh4snmyo72lc5uy.png" alt="Top-level AWS CDK Stack Design with Nested" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this refined architecture, one top-level AWS CDK stack contains 500 AWS CDK nested stacks. Each AWS CDK nested stack containing 500 resources results in 250,000 Amazon S3 buckets that can be created in a single top-level stack. The team can solve the original goal by creating four top-level stacks. The result is much simpler management, as the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-console-view-stack-data-resources.html" rel="noopener noreferrer"&gt;AWS CloudFormation console&lt;/a&gt; offers filtering of the nested stacks, which at this point are an implementation detail not interesting in the daily operations of the solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn82e82575wz3odtvk33h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn82e82575wz3odtvk33h.png" alt="AWS CloudFormation Console - View Nested" width="584" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, platform engineers encounter an error when attempting to deploy this solution. The next section describes the resolution step that the team must take.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refinement 2 - Deployable State
&lt;/h3&gt;

&lt;p&gt;With the top-level stack hierarchy introduced, AWS CloudFormation attempts to create 250,000 AWS resources in parallel. The team finds out that this is &lt;strong&gt;100 times&lt;/strong&gt; over the hard limit of the maximum number of AWS CloudFormation resources a nested stack can create, update, or delete per operation (details under &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html" rel="noopener noreferrer"&gt;Nested stacks&lt;/a&gt;). To avoid crossing this limit and maintain the current stack structure, platform engineers figured out that the nested stacks need to depend on each other, as presented on the diagram below by the arrows between each nested stack. The dependency is from the AWS CDK stack that needs to depend on another AWS CDK stack and can be added using &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/stacks.html#stack_api" rel="noopener noreferrer"&gt;&lt;code&gt;stack.addDependency(stack)&lt;/code&gt;&lt;/a&gt; stack API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsahs1inqx4fjq34f2xh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsahs1inqx4fjq34f2xh.png" alt="Top-level AWS CDK Stack Design with Nested and Dependencies" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the dependencies indicated between the AWS CDK nested stacks, each nested stack is created and updated sequentially, starting with the first one. Only after the first one is done processing, the second one proceeds, and so on, until the last one. This means 500 resources are created, updated, or deleted at most at any given time, staying within the 2,500 hard limit. The team can now successfully deploy the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refinement 3 - Production Readiness
&lt;/h3&gt;

&lt;p&gt;The platform engineering team can take the solution one step further to prepare it for real-world scenarios where Amazon S3 buckets will be removed and created regularly. From the original example, imagine a dataset that is no longer needed. In such a case, the corresponding Amazon S3 bucket can be deleted. However, if such a deletion occurs, it creates an empty slot in the AWS CDK stack hierarchy, and it would be optimal to use that slot the next time a new Amazon S3 bucket needs to be created.&lt;/p&gt;

&lt;p&gt;To achieve the ability to dynamically discover empty slots in the AWS CDK stack hierarchy, &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; can be utilised to create a lookup table containing the information on where each Amazon S3 bucket is currently deployed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nested Stack&lt;/th&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Bucket Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;KodlotBucket1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;KodlotBucket2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;KodlotBucket3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;KodlotBucket500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack500&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;KodlotBucket249501&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KodlotS3NestedStack500&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;KodlotBucket250000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Assuming that &lt;code&gt;KodlotBucket3&lt;/code&gt; gets decommissioned, the lookup table is going to reflect that by having the item corresponding to composite key &lt;code&gt;Nested Stack=KodlotS3NestedStack1&lt;/code&gt;, &lt;code&gt;Slot=3&lt;/code&gt; removed. The team needs to add a script executing after the deployment that updates the lookup table with the current state of the deployed resources. They also adjust the AWS CDK code to use the lookup table to allocate the resources. It makes the infrastructure code more complex and adds a network lookup as a part of the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/configure-synth.html" rel="noopener noreferrer"&gt;AWS CDK stack synthesis&lt;/a&gt;. Without the network connection and valid AWS access keys, platform engineers can't synthesise the stacks with this solution. This makes local development harder, and as the team matures, they start exploring how to address it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Refinement 4 - Removing the AWS Account Lookup
&lt;/h4&gt;

&lt;p&gt;To overcome the last obstacle of not requiring the connection to the AWS account in the infrastructure code, platform engineers substitute the Amazon DynamoDB lookup table with the &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;AWS SSM parameters&lt;/a&gt;. This is because AWS CDK can handle the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/get_ssm_value.html" rel="noopener noreferrer"&gt;AWS SSM parameter lookup without the connection to the AWS account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The team can create a lookup mechanism based on AWS SSM parameters with the following AWS SSM parameters using its hierarchical structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;kodlot/nestedstack1/slot1/bucketname = KodlotBucket1
kodlot/nestedstack1/slot2/bucketname = KodlotBucket2
kodlot/nestedstack1/slot3/bucketname = KodlotBucket3
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;kodlot/nestedstack1/slot500/bucketname = KodlotBucket500
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;kodlot/nestedstack500/slot1/bucketname = KodlotBucket249501
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;kodlot/nestedstack500/slot500/bucketname = KodlotBucket250000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The platform engineering team discovers that there is a limit to this approach as well. AWS SSM has two types of parameters: standard and advanced. Currently, there can be 10,000 standard and 100,000 advanced parameters created per AWS account and AWS Region (details &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-advanced-parameters.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;). When creating 1 million Amazon S3 buckets, assigning an AWS SSM parameter to each Amazon S3 bucket is impossible. Since AWS SSM parameters allow sizes of 4KB for standard and 8KB for advanced parameters, the solution needs to bundle, e.g. each nested stack slots together into a single parameter, making it harder to manage. This path is for those platform engineers who want to explore this possibility, especially if the requirement is to create more than 100,000 Amazon S3 buckets crossing the limit of the number of AWS SSM parameters. It is worth it, as it will bring valuable lessons for the team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Engineering scalable infrastructure as code solutions entails various challenges that can be seen as opportunities for learning and considering when architecting similar solutions. Platform engineering teams will face challenges described here and similar ones as they support the continuously growing needs. Through this experience, mature solutions are developed, and the platform engineering teams' problem-solving ability is developed. The creative mindset is key for exploring available options, combined with knowledge of the infrastructure as code tools and cloud services that can support creating the desired outcome. The right investments in ongoing learning for the teams are crucial, topped with the clear support of exploration and treating failures as a feedback loop for the future.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>awscdk</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>AWS CDK Validation in Python</title>
      <dc:creator>Krzysztof Slowinski</dc:creator>
      <pubDate>Mon, 20 Jan 2025 08:08:43 +0000</pubDate>
      <link>https://dev.to/krzyslow/aws-cdk-validation-in-python-4d5i</link>
      <guid>https://dev.to/krzyslow/aws-cdk-validation-in-python-4d5i</guid>
      <description>&lt;p&gt;Validation is a part of the AWS CDK app lifecycle described in the official documentation under the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/deploy.html#deploy-how-synth" rel="noopener noreferrer"&gt;CDK app synthesis&lt;/a&gt; section:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.richardneililagan.com/posts/aws-cdk-validation-checks/" rel="noopener noreferrer"&gt;Validation checks in AWS CDK constructs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/validation-with-aws-cdk-addvalidation-20lo"&gt;Validation with AWS CDK (addValidation)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following examples we developed using &lt;a href="https://github.com/aws/aws-cdk/releases/tag/v2.176.0" rel="noopener noreferrer"&gt;AWS CDK v2.176.0&lt;/a&gt; and &lt;a href="https://www.python.org/downloads/release/python-3131/" rel="noopener noreferrer"&gt;Python 3.13.1&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Validation
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/python/constructs/IValidation.html" rel="noopener noreferrer"&gt;&lt;code&gt;@jsii.implements(IValidation)&lt;/code&gt;&lt;/a&gt;, 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@jsii.implements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IValidation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotS3KmsValidator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Checking that the S3 bucket is encrypted with a custom KMS key.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_bucket&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encryption_key&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bucket must be encrypted with a custom KMS key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;error_messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS CDK Construct Example
&lt;/h3&gt;

&lt;p&gt;This is a basic example of an AWS CDK construct adding a single validation using the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/python/constructs/Node.html#constructs.Node.add_validation" rel="noopener noreferrer"&gt;&lt;code&gt;add_validation&lt;/code&gt;&lt;/a&gt; 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 &lt;code&gt;encryption_key&lt;/code&gt; parameter of the Amazon S3 &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_s3/Bucket.html" rel="noopener noreferrer"&gt;&lt;code&gt;Bucket&lt;/code&gt;&lt;/a&gt; construct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotS3Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;S3 bucket with validations.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;s3_kms_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KodlotS3KmsKey&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KodlotS3&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;encryption_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_validation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KodlotS3KmsValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This type of mistake could easily be overlooked, and that's where the validation comes in help. Running CDK &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-synth.html" rel="noopener noreferrer"&gt;synthesis&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-deploy.html" rel="noopener noreferrer"&gt;deployment&lt;/a&gt; with this example results in the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;RuntimeError: Error: Validation failed with the following errors:
  [KodlotS3Stack/KodlotBucket1] Bucket must be encrypted with a custom KMS key
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multiple Validations
&lt;/h2&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@jsii.implements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IValidation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotS3NotWebsiteValidator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Checking that the S3 bucket is NOT configured for static website hosting.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_bucket&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_website&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bucket must not be used for static website hosting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;error_messagesAWS&lt;/span&gt; &lt;span class="n"&gt;CDK&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="n"&gt;Updated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS CDK Construct Example Updated
&lt;/h3&gt;

&lt;p&gt;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 &lt;code&gt;website_index_document&lt;/code&gt; to the Amazon S3 &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_s3/Bucket.html" rel="noopener noreferrer"&gt;&lt;code&gt;Bucket&lt;/code&gt;&lt;/a&gt; construct, indicating that it will be used for static website hosting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotS3Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;S3 bucket with validations.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;s3_kms_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KodlotS3KmsKey&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KodlotS3&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;encryption_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;website_index_document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_validation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KodlotS3KmsValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_validation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KodlotS3NotWebsiteValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_bucket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-synth.html" rel="noopener noreferrer"&gt;synthesis&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-deploy.html" rel="noopener noreferrer"&gt;deployment&lt;/a&gt; with this example results in the following errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;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
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS CDK Stack Validation
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_lambda/Function.html" rel="noopener noreferrer"&gt;&lt;code&gt;Function&lt;/code&gt;&lt;/a&gt; using Python's built-in &lt;a href="https://docs.python.org/3/library/functions.html#isinstance" rel="noopener noreferrer"&gt;&lt;code&gt;isinstance&lt;/code&gt;&lt;/a&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@jsii.implements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IValidation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotNoLambdaInStackValidator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Checking that NO Lambda function was defined in a given stack.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;construct&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;error_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stack must not contain any Lambda functions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;error_messages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS CDK Stack Example
&lt;/h3&gt;

&lt;p&gt;To add the validation for the stack, we use the same &lt;code&gt;add_validation&lt;/code&gt; method as for the constructs. Since &lt;code&gt;KodlotS3Stack&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KodlotS3Stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Stack containing S3 buckets.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nc"&gt;KodlotS3Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KodlotBucket1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kodlot1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;inline_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
def handler(event, context):
    return 200
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyLambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_inline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inline_code&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_validation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KodlotNoLambdaInStackValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running CDK &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-synth.html" rel="noopener noreferrer"&gt;synthesis&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-deploy.html" rel="noopener noreferrer"&gt;deployment&lt;/a&gt; with this example results in the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;RuntimeError: Error: Validation failed with the following errors:
  [KodlotS3Stack] Stack must not contain any Lambda functions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Built-in validations
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;k1&lt;/code&gt;, resulting in an illegal name length, there will be the following build-in error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;RuntimeError: Invalid S3 bucket name (value: k1)
Bucket name must be at least 3 and no more than 63 characters
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, if we configure the bucket name of &lt;code&gt;k_1&lt;/code&gt;, resulting in an illegal underscore character present in it, there will be the following build-in error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;RuntimeError: Invalid S3 bucket name (value: k_1)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 3)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;AWS CDK validation is a helpful mechanism for developers to use in conjunction with &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/testing.html" rel="noopener noreferrer"&gt;unit&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/devops/how-to-write-and-execute-integration-tests-for-aws-cdk-applications/" rel="noopener noreferrer"&gt;integration&lt;/a&gt; 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.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>awscdk</category>
      <category>cdk</category>
      <category>python</category>
    </item>
  </channel>
</rss>
