Resource Tagging and Handling Key:Value Casing

Upper and lower case characters can lead to false positives. With Guardrails, we can use calculated policies to check for tags regardless of casing, reducing false positives and allowing users and administrators to focus on real business improvements. Additionally, a calculated policy can be written to automatically fix values that should be either all lower or all upper case. Note that existing keys CANNOT be changed (this is done to protect any existing tag).

This example, along with many others, can be found in the Guardrails Samples Repo

Initial Configuration

For our initial configuration, let us assume we have an AWS S3 bucket called turbot-test-bucket with the following tags:

KeyValue
TestTag
environmentDEV
ownerjohn Doe

Assume that we have also set the policy AWS > S3 > Bucket > Tags to Check: Tags are correct. This means that Guardrails will only alarm and not change any tags on the resource itself.

Tagging with Guardrails

Check Tags, Ignoring Casing

Using Guardrails, we can define a policy AWS > S3 > Bucket > Tags > Template with logic that is case agnostic.

First, let's start with the GraphQL query. We want to just pull in the resource tags:

{
resource {
turbot {
tags
}
}
}

If our template looked like the following:

{%- if $.resource.turbot.tags['owner'] == 'john doe' -%}
[]
{%- else -%}
- 'owner': 'invalid_tag'
{%- endif -%}

Guardrails would evaluate the control AWS > S3 > Bucket > Tags to Alarm due to the casing on the tag value john Doe. To get around this, we use RegEx to evaluate the string. If we are confident in the casing of the key, it is possible to do something simple like so:

{%- if r/john doe/.test($.resource.turbot.tags['owner'] | lower) -%}
[]
{%- else -%}
- 'owner': 'invalid_tag'
{%- endif -%}

However, real applications are usually not so trivial. To account for the casing differences with the key AND value, we need to add some additional lines of code:

{%- set approved = 'no' -%}
{%- for key,value in $.resource.turbot.tags -%}
{%- if r/owners/.test(key | lower) -%}
{%- if r/john doe/.test(value | lower) %}
{%- set approved = 'yes' -%}
{%- endif %}
{%- endif -%}
{%- endfor -%}
{%- if approved == 'no' -%}
- owner: 'Missing_tag'
{%- else -%}
[]
{%- endif -%}

First, set a variable that is used to denote if the key: value pair exists. Then, parse out the key and value pairs using a for loop, check for the key first, and if the key exists, check for the value. If both the key and value if statement evaluate to true, we set the dummy variable to yes and in the final if statement, simply pass an empty array denoting the tags are approved. Because our bucket has the key:value pair Owners:john Doe, it passes with flying colors!

Check Tags, Change Casing

Using Guardrails, we can define a policy AWS > S3 > Bucket > Tags > Template with logic that is case agnostic.

As above, let's start with the GraphQL query. We want to just pull in the resource tags:

{
resource {
turbot {
tags
}
}
}

We want to check to make sure the key owner exists, then change the value to be all lower case.

{%- set regExp = r/owners/ -%}
{%- set set_key = 0 -%}
{%- for key, value in $.resource.turbot.tags -%}
{%- set lower_value = (value | lower) -%}
{%- if regExp.test(key | lower) and lower_value != value -%}
- {{ key }}: {{ lower_value }}
{%- set set_key = 1 -%}
{%- endif -%}
{%- endfor -%}
{%- if set_key == 0 -%}
[]
{%- endif -%}

In this template, set a variable that can be used as a boolean that changes value only if the lower case tag change is necessary. If there is no change, we want to be sure to output a blank array. We can utilize Regex to test the key to ensure we are looking at the right one, then test the existing value with another string that has had the lower function applied. If those strings don't match, the result of the template is to change the current value with the lower case value.