Skip to main content

Subpolicies

Let's continue with our example, and imagine we now have a policy with a lot of checks we'd like to run against our cloud deployment (All example queries are taken from our AWS Foundational Security Best Practices policy):

my_second_policy/policy.hcl
policy "my_second_policy" {
title = "My Second CloudQuery Policy"

configuration {
provider "aws" {
version = ">= 0.10.0"
}
}

check "old-stopped-ec2-instances" {
title = "Stopped EC2 instances should be removed after a specified time period"
query = "SELECT account_id, region, id FROM aws_ec2_instances WHERE state_name = 'stopped' AND NOW() - state_transition_reason_time > INTERVAL '30' DAY"
expect_output = false
}

check "ec2-instances-with-public-ip-addresses" {
title = "EC2 instances shouldn't have a public IP address"
query = "SELECT account_id, region, id FROM aws_ec2_instances WHERE public_ip_address IS NOT NULL"
expect_output = false
}

check "efs-encrypts-data-at-rest" {
title = "EFS should be configured to encrypt file data at rest using AWS KMS"
query = "SELECT account_id, arn FROM aws_efs_filesystems WHERE encrypted IS DISTINCT FROM TRUE OR kms_key_id IS NULL"
expect_output = false
}

check "efs-volumes-are-in-backup-plans" {
title = "EFS volumes should be in backup plans"
query = "SELECT account_id, arn FROM aws_efs_filesystems WHERE backup_policy_status IS DISTINCT FROM 'ENABLED'"
expect_output = false
}
}

As the policy expands, we might want to split it into multiple subpolicies - for instance, an ec2 subpolicy, and an efs subpolicy (this is similar to the structure of our foundational_security policy). The subpolicies can then be run separately.

Our policy.hcl will now become:

my_second_policy/policy.hcl
policy "my_second_policy" {
title = "My Second CloudQuery Policy"

configuration {
provider "aws" {
version = ">= 0.10.0"
}
}

policy "ec2" {
source = file("./ec2_subpolicy.hcl")
}

policy "efs" {
source = file("./efs_subpolicy.hcl")
}
}

We will create two more files in our my_second_policy directory: ec2_subpolicy.hcl and efs_subpolicy.hcl:

my_second_policy/ec2_subpolicy.hcl
policy "ec2" {
title = "EC2 subpolicy"

configuration {
provider "aws" {
version = ">= 0.10.0"
}
}

check "old-stopped-ec2-instances" {
title = "Stopped EC2 instances should be removed after a specified time period"
query = "SELECT account_id, region, id FROM aws_ec2_instances WHERE state_name = 'stopped' AND NOW() - state_transition_reason_time > INTERVAL '30' DAY"
expect_output = false
}

check "ec2-instances-with-public-ip-addresses" {
title = "EC2 instances shouldn't have a public IP address"
query = "SELECT account_id, region, id FROM aws_ec2_instances WHERE public_ip_address IS NOT NULL"
expect_output = false
}
}
my_second_policy/efs_subpolicy.hcl
policy "efs"{
title = "EFS subpolicy"

configuration {
provider "aws" {
version = ">= 0.10.0"
}
}

check "efs-encrypts-data-at-rest" {
title = "EFS should be configured to encrypt file data at rest using AWS KMS"
query = "SELECT account_id, arn FROM aws_efs_filesystems WHERE encrypted IS DISTINCT FROM TRUE OR kms_key_id IS NULL"
expect_output = false
}

check "efs-volumes-are-in-backup-plans" {
title = "EFS volumes should be in backup plans"
query = "SELECT account_id, arn FROM aws_efs_filesystems WHERE backup_policy_status IS DISTINCT FROM 'ENABLED'"
expect_output = false
}
}

We can then run our sub-policies separately with the following commands (./my_second_policy is the path to the my_second_policy_directory):

cloudquery policy run ./my_second_policy//ec2

cloudquery policy run ./my_second_policy//efs

We can also run a single query separately with:

cloudquery policy run ./my_second_policy//ec2/old-stopped-ec2-instances

What is "//"? How is it different from "/" and where should it go?

The // indicator when running subpolicies or subqueries is an idiosyncrasy of the way the cloudquery CLI works. It separates the "path to the policy" from the "path to the subpolicy (in the policy)".

So, in our above example, if the policy I'd like to run is in ./my_second_policy, and I would like to run the old-stopped-ec2-instances query in the ec2 subpolicy, I would use:

cloudquery policy run ./my_second_policy//ec2/old-stopped-ec2-instances

It is worth mentioning here that the cloudquery CLI also supports running policies from our official github. The // separator serves the same function described above - separating the "path to the policy" from the "path inside the policy". In case of running policies from github, it helps the cloudquery CLI to know which repo to clone.

So, to run the foundational_security subpolicy in the the aws policy, we run.

cloudquery policy run aws//foundational_security
Summary

When calling cloudquery policy run, the // separator separates the "path to the root policy" from the "path to the subpolicy/subquery". It should always appear after the root policy.

Views and Subpolicies

Views are scoped to the (sub)policies they are defined in. i.e. you cannot reference a view defined in parent or child subpolicies. To avoid code duplication when reusing views, it is recommended to use query = file("./path_to_view.sql") . For example:

policy "some-policy"{
title = "Some Policy"

configuration {
...
}

policy "subpolicy-1" {
view "someview"{
title = "Some View"
query = file("./path_to_some_view.sql")
}

# checks using "someview" go here
...
}

policy "subpolicy-2" {
view "someview"{
title = "Some View"
query = file("./path_to_some_view.sql")
}

# checks using "someview" go here
...
}

}