How To

Customization guide for the AWS Well Architected Mod: Part 2

Learn how to add support for a new pillar, Sustainability, using a custom control.

Steampipe Team
6 min. read - Jul 12, 2023
Learn how to add support for a new pillar, Sustainability, using a custom control.

In part 1 we showed how you can extend the AWS Well-Architected mod with a benchmark that helps you assess your infrastructure for enviromental impacts targeted by the Sustainability pillar. The AWS Well-Architected mod is an example of how to reuse and remix existing benchmarks and dashboards: it cherrypicks controls from AWS Compliance which provides more than 780 controls. The Sustainability benchmark we added in part 1 follows the same strategy: find and reuse existing controls. But what if you want to check something that isn't covered by an existing control? It's easy to add your own custom controls, and in this post we'll show you how.

The SUS 4 section of the Well-Architected framework relates to data. One of the recommended best practices is SUS04-BP02 Use technologies that support data access and storage patterns. To align with that best practice, you may want to take advantage of S3 Intelligent-Tiering storage. There isn't yet an existing control that checks whether the feature is enabled for your S3 buckets. So let's create one!

Fork and clone the AWS Well-Architected mod

To get started, create a fork of the mod in your GitHub account. Then clone it.

git clone https://github.com/judell/steampipe-mod-aws-well-architected
cd steampipe-mod-aws-well-architected

And install the dependent mod, AWS Compliance.

$ steampipe mod install
Installed 1 mod:
aws_well_architected
└── github.com/turbot/steampipe-mod-aws-compliance@v0.67.0

If you do nothing else at this point, running steampipe dashboard will display both AWS Compliance and AWS Well-Architected just as happens when you use the AWS Well-Architected mod directly.

Define a new second-level benchmark for the Sustainability pillar

The existing pillars live in subdirectories of well-architected-framework. Create a new subdirectory there, called sustainability, and within it a new file, sustainability.sp:

locals {
well_architected_framework_sustainability_common_tags = merge(local.well_architected_framework_common_tags, {
pillar_id = "sustainability"
})
}
benchmark "well_architected_framework_sustainability" {
title = "sustainability"
description = "The Sustainability pillar addresses the long-term environmental, economic, and societal impact of your business activities."
children = [
benchmark.well_architected_framework_sus04
]
tags = local.well_architected_framework_sustainability_common_tags
}

We've yet to define benchmark.well_architected_framework_sus04. But first, refer to the new benchmark from the top-level well_architected_framework.sp:

benchmark "well_architected_framework" {
title = "AWS Well-Architected Framework"
description = "The AWS Well-Architected Framework describes key concepts, design principles, and architectural best practices for designing and running workloads in the cloud. By answering a few foundational questions, learn how well your architecture aligns with cloud best practices and gain guidance for making improvements."
documentation = file("./well_architected_framework/docs/well_architected_framework_overview.md")
children = [
benchmark.well_architected_framework_operational_excellence,
benchmark.well_architected_framework_reliability,
benchmark.well_architected_framework_security,
benchmark.well_architected_framework_sustainability
]
tags = local.well_architected_framework_common_tags
}

Define a third-level benchmark for a topic

Now create a new file, sus04.sp, which defines benchmark.well_architected_framework_sus04 as cited above.

locals {
well_architected_framework_sus04_common_tags = merge(local.well_architected_framework_sustainability_common_tags, {
question_id = "sus_data"
})
}
benchmark "well_architected_framework_sus04" {
title = "SUS04 How do you take advantage of data management policies and patterns to support your sustainability goals?"
description = "Implement data management practices to reduce the provisioned storage required to support your workload, and the resources required to use it. Understand your data, and use storage technologies and configurations that more effectively support the business value of the data and how it’s used. Lifecycle data to more efficient, less performant storage when requirements decrease, and delete data that’s no longer required."
children = [
control.s3_buckets_use_intelligent_tiering
]
tags = merge(local.well_architected_framework_sus04_common_tags, {
choice_id = "sus_sus_data_a3"
})
}

Refer to the Aligning tags to see how you can use the aws_wellarchitected_answer table to find values for question_id and choice_id.

If we were cherrypicking existing controls, their names would be fully qualified in the children list. In part 1, for example, we reused a control from the AWS Thrifty mod like so.

children = [
aws_thrifty.control.ecs_cluster_low_utilization,
]

In this case, our custom control belongs to the mod we're creating so we don't need to fully qualify its name. Note that, as per this example from the documentation, you can mix both flavors in a benchmark's children list:

benchmark "my_mod_public_resources" {
title = "Public Resources"
description = "Resources that are public."
children = [
aws_compliance.control.dms_replication_instance_not_publicly_accessible,
aws_compliance.control.s3_bucket_restrict_public_write_access,
control.my_mod_public_ec2,
]
}

Define a new control

We've defined the well_architected_framework_sus04 benchmark to expect a control called s3_buckets_use_intelligent_tiering. Like all controls, it will be powered by a query that returns three required columns: status, reason, and resource. Here's a query that enumerates S3 buckets, and joins with the [aws_s3_bucket_intelligent_tiering_configuration] (https://hub.steampipe.io/plugins/turbot/aws/tables/aws_s3_bucket_intelligent_tiering_configuration) table.

query "s3_buckets_use_intelligent_tiering" {
sql = <<EOQ
select
b.name as resource,
case when i.status = 'Enabled' then 'ok' else 'alarm' end as status,
case when i.status = 'Enabled'
then b.name || ' uses intelligent tiering.'
else b.name || ' does not use intelligent tiering.'
end as reason,
b.account_id,
b.region
from
aws_s3_bucket b
left join
aws_s3_bucket_intelligent_tiering_configuration i
on
b.name = i.bucket_name
EOQ
}

The new control refers to that query.

control "s3_buckets_use_intelligent_tiering" {
title = "Use S3 intelligent tiering"
description = "S3 buckets should use intelligent tiering"
query = query.s3_buckets_use_intelligent_tiering
}

Per the documentation, a control can use either query = as we're doing here to refer to a separately-defined query, or sql = to include the query within the control. We recommend the query approach, so that a query like s3_buckets_use_intelligent_tiering is also avaiable in other contexts.

And voilà! The new benchmark is up and running.

Next steps

You can, of course, use your fork of the Well-Architected mod privately. But we hope you'll want to contribute new mappings that improve the Well-Architected mod for everyone. For example, here's a pull request for the new benchmark and custom control we've defined here.

Do you have other ideas for extending the Well-Architected mod? Give them a try and let us know how it goes!