Leveraging Cloud Custodian to shift left tag compliance

Search for a command to run...

No comments yet. Be the first to comment.
Terraform state locking as referred from Terraform State Locking documentation is a feature where Terraform will lock your state for all operations that could write state. This prevents others from acquiring the lock and potentially corrupting your s...

Provisioning Amazon Web Services (AWS) resources came in many ways. One of them is create the resource from the console or some might say "clickops". But how if We want our resources that created in clickops way, later maintained in more codified way...

Tagging is one of the capability that included in Establishing Your Cloud Foundation on AWS whitepaper under category Governance, Risk Management, and Compliance.
Tagging enables you to group sets of resources by assigning metadata to cloud resources for a variety of purposes. These purposes include access control (such as ABAC), cost reporting, and automation (such as patching for select tagged instances). Tagging can also be used to create new resource constructs for visibility or control (such as grouping together resources that make up a microservice, application, or workload). Tagging is fundamental to providing enterprise-level visibility and control.
There are several use cases why we need tagging as explained in this document “Tagging use cases“, among them:
Tags for cost allocation and financial management
Tags for operations and support
Tags for data security, risk management, and access control
In this blog, I will explain how to shift left tag compliance check inside CI/CD. I leverage Cloud Custodian package called c7n-left to run policy directly against Infrastructure as Code which in this case is Terraform. If tag compliance check is passed then Terraform core workflow which consist plan and apply can be continued. Otherwise, if tag compliance check is failed then Terraform core workflow cannot be continued. CI/CD tool that I used in this blog is Amazon CodeCatalyst.

Disclaimer: All contents written in this blog are solely my personal opinion and doesn’t represent my employer opinion
Here are the prerequisites that needed to follow configurations discussed in this blog:
AWS account
CodeCatalyst space and project that has been connected with AWS account and source repository. In this blog, I use GitHub as source repository. For configuration reference, please check my other blog "Build and Release Container Image to Amazon Elastic Container Registry (ECR) via Amazon CodeCatalyst"
S3 bucket and DynamoDB table that will be used for Terraform backend
Below is directory structure used in this blog:
.
├── .codecatalyst/
│ └── workflows/
│ └── tf-sandbox-sbx0-ec2.yml
├── custodian/
│ └── tf-tag-compliance.yml
└── sandbox/
└── sbx0/
└── ec2/
├── backend.tf
├── main.tf
├── terraform.tfvars
└── variables.tf
We will start discussion with policy that will be used to evaluate Infrastructure as Code by Cloud Custodian
custodian, I created file called tf-tag-compliance.yml. Inside that file there is a policy named check-tag that will check all Terraform codes that related to AWS resources. Policy will check if tag called lanandra:environment is absent or not on all resources that taggable. I also added metadata to that policypolicies:
- name: check-tag
resource: "terraform.aws*"
metadata:
category: [tag]
severity: low
filters:
- taggable
- or:
- type: value
key: "tag:lanandra:environment"
value: absent
As an example, I created two EC2 instances, one that has tag lanandra:environment and the other doesn’t
Create Terraform files inside directory sandbox/sbx0/ec2 , main resources such as EC2 are defined inside main.tf file
EC2 that has tag lanandra:environment , this tag is got by merging local tags.
locals {
azs = slice(data.aws_availability_zones.available.names, 0, 3)
tags = {
"lanandra:environment" = "${var.env_name}"
"lanandra:managedBy" = "terraform"
}
}
module "lab_ec2_0" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.6.1"
name = "${var.env_name}-lab-0"
ami = data.aws_ami.ubuntu_22_04.id
instance_type = "t3a.nano"
availability_zone = element(local.azs, 0)
subnet_id = element(var.public_subnet_id, 0)
vpc_security_group_ids = [module.lab_sg.security_group_id]
associate_public_ip_address = true
key_name = var.env_name
iam_instance_profile = module.lab_instance_profile.iam_instance_profile_name
enable_volume_tags = false
root_block_device = [
{
encrypted = true
volume_type = "gp3"
volume_size = 10
tags = merge(
local.tags,
{
Name = "${var.env_name}-lab-0"
}
)
}
]
tags = merge(
local.tags,
{
Name = "${var.env_name}-lab-0"
}
)
}
lanandra:environmentmodule "lab_ec2_1" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.6.1"
name = "${var.env_name}-lab-1"
ami = data.aws_ami.ubuntu_22_04.id
instance_type = "t3a.nano"
availability_zone = element(local.azs, 0)
subnet_id = element(var.public_subnet_id, 0)
vpc_security_group_ids = [module.lab_sg.security_group_id]
associate_public_ip_address = true
key_name = var.env_name
iam_instance_profile = module.lab_instance_profile.iam_instance_profile_name
enable_volume_tags = false
root_block_device = [
{
encrypted = true
volume_type = "gp3"
volume_size = 10
tags = {
Name = "${var.env_name}-lab-1"
}
}
]
tags = {
Name = "${var.env_name}-lab-1"
}
}
As mentioned in the beginning of this blog, I use Amazon CodeCatalyst as tool for CI/CD pipeline.
tf-sandbox-sbx0-ec2.yml and I put that inside .codecatalyst/workflow directory. Inside workflow/pipeline, I created action called tag-compliance. This action use container image python:3.12.8-bookworm that being pulled from Dockerhub. First, it will redirect working directory to ec2 directory (sandbox/sbx0/ec2). Then it will run several steps that needed to run tag compliance check using Cloud Custodian. In this case, I use specific package called c7n-left that designed to evaluate policies directly against infrastructure as code source assets (Please see Custodian policies for Infrastructure Code for more details). Then it will run command that will find custodian policy inside custodian directory with category value filtered to tag tag-compliance:
Identifier: aws/build@v1
Inputs:
Sources:
- WorkflowSource
Environment:
Name: sandbox
Connections:
- Name: lanandra-sandbox
Role: tf-codecatalyst-admin-sandbox
Configuration:
Container:
Registry: DockerHub
Image: python:3.12.8-bookworm
Steps:
- Run: cd sandbox/sbx0/ec2
- Run: pip install c7n
- Run: pip install c7n-left
- Run: c7n-left run -p ../../../custodian/ -d . --filters="category=tag"
Compute:
Type: EC2
Name: tf-sandbox-sbx0-ec2
SchemaVersion: "1.0"
Triggers:
- Type: PUSH
Branches:
- master
FilesChanged:
- sandbox\/sbx0\/ec2\/.*
Actions:
tag-compliance:
Identifier: aws/build@v1
Inputs:
Sources:
- WorkflowSource
Environment:
Name: sandbox
Connections:
- Name: lanandra-sandbox
Role: tf-codecatalyst-admin-sandbox
Configuration:
Container:
Registry: DockerHub
Image: python:3.12.8-bookworm
Steps:
- Run: cd sandbox/sbx0/ec2
- Run: pip install c7n
- Run: pip install c7n-left
- Run: c7n-left run -p ../../../custodian/ -d . --filters="category=tag"
Compute:
Type: EC2
terraform-plan:
Identifier: aws/build@v1
DependsOn:
- tag-compliance
Inputs:
Sources:
- WorkflowSource
Environment:
Name: sandbox
Connections:
- Name: lanandra-sandbox
Role: tf-codecatalyst-admin-sandbox
Configuration:
Container:
Registry: DockerHub
Image: hashicorp/terraform:1.8.2
Steps:
- Run: cd sandbox/sbx0/ec2
- Run: terraform fmt -check -no-color
- Run: terraform init -no-color
- Run: terraform validate -no-color
- Run: terraform plan -no-color -input=false
Compute:
Type: EC2
wait-for-approval:
Identifier: aws/approval@v1
DependsOn:
- terraform-plan
Configuration:
ApprovalsRequired: 1
terraform-apply:
DependsOn:
- wait-for-approval
Identifier: aws/build@v1
Inputs:
Sources:
- WorkflowSource
Environment:
Name: sandbox
Connections:
- Name: lanandra-sandbox
Role: tf-codecatalyst-admin-sandbox
Configuration:
Container:
Registry: DockerHub
Image: hashicorp/terraform:1.8.2
Steps:
- Run: cd sandbox/sbx0/ec2
- Run: terraform init -no-color
- Run: terraform apply -auto-approve -no-color -input=false
Compute:
Type: EC2






We have reached the last section of this blog. Here are some key takeaways that can be summarized:
We can shift left our tag compliance check inside CI/CD
Cloud Custodian is one of the tool that can be leveraged to run tag compliance check
Cloud Custodian has specific package called c7n-left to run policy check directly against Infrastructure code
Please comment if you have any suggestions, critiques, or thoughts.
Hope this article will benefit you. Thank you!