Tag Resources with Dynamic Metadata
Org control objectives often include the need to tag resources with metadata of the resource or dynamic metadata from other sources, such as a Guardrails File. This guide will go over three different scenarios:
- Tag an S3 bucket with an attribute in the bucket metadata.
- Tag an S3 bucket with attributes stored in a Guardrails File.
- Tag an S3 bucket with attributes from a Guardrails Folder
Initial Configuration
For our initial configuration, let us assume we have an AWS S3 bucket called
turbot-test-bucket
.
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.
Each example will be defining a calculated policy using AWS > S3 > Bucket > Tags > Template.
Tagging Resources with CMDB Metadata
This example will use the following S3 bucket metadata:
S3 bucket metadata
AccelerateConfiguration:Status: SuspendedAcl:Grants: - Grantee: ID: 6eb349968d9164c06c9c28123456sdfoadjf09a9ee8f79cb36157431f5e9713f Type: CanonicalUser Permission: FULL_CONTROLOwner: ID: 6eb349968d9164c06c9c28123456sdfoadjf09a9ee8f79cb36157431f5e9713fCors: {}CreationDate: '2020-07-20T17:07:32.000Z'Encryption:ServerSideEncryptionConfiguration: Rules: - ApplyServerSideEncryptionByDefault: KMSMasterKeyID: alias/turbot/default SSEAlgorithm: 'aws:kms' BucketKeyEnabled: falseLifecycle:- Filter: And: Prefix: test Tags: - Key: test Value: value ID: new-rule NoncurrentVersionTransitions: [] Status: Enabled Transitions: - Days: 9999 StorageClass: STANDARD_IALocationConstraint: us-east-2Logging: {}Name: turbot-test-bucketNotificationConfiguration: {}Payer: BucketOwnerPolicy:Statement: - Action: 's3:GetBucketAcl' Effect: Allow Principal: Service: cloudtrail.amazonaws.com Resource: 'arn:aws:s3:::turbot-test-bucket' Sid: AWSCloudTrailAclCheck20150319 - Action: 's3:PutObject' Condition: StringEquals: 's3:x-amz-acl': bucket-owner-full-control Effect: Allow Principal: Service: cloudtrail.amazonaws.com Resource: 'arn:aws:s3:::turbot-test-bucket/AWSLogs/12345678012/*' Sid: AWSCloudTrailWrite20150319 - Action: 's3:*' Effect: Allow Principal: Service: config.amazonaws.com Resource: - 'arn:aws:s3:::turbot-test-bucket' - 'arn:aws:s3:::turbot-test-bucket/*' Sid: ConfigRule - Action: 's3:*' Condition: Bool: 'aws:SecureTransport': 'false' Effect: Deny Principal: '*' Resource: - 'arn:aws:s3:::turbot-test-bucket' - 'arn:aws:s3:::turbot-test-bucket/*' Sid: MustBeEncryptedInTransitVersion: '2012-10-17'PolicyStatus:IsPublic: falsePublicAccessBlock:BlockPublicAcls: falseBlockPublicPolicy: falseIgnorePublicAcls: falseRestrictPublicBuckets: falseReplication: {}Tags:- Key: Owners Value: john Doe- Key: Test Value: ''- Key: environment Value: DEVVersioning:MFADelete: DisabledStatus: SuspendedWebsite: {}
In this example, we want to ensure that the payer is a tag on the bucket, i.e.
the key:value pair payer
:BucketOwner
. Let's start with the GraphQL query:
{ resource { payer: get(path: "Payer") turbot { tags } }}
This gives us the following output:
{ "resource": { "region": "us-east-2", "turbot": { "tags": { "Test": "", "Owners": "john Doe", "environment": "DEV" } } }}
Note that we can use this get: path()
function to call a specific attribute.
This example uses Payer
and we call the attribute payer
.
Finally, to implement the logic, we use this nunjucks template:
{%- if $.resource.turbot.tags['payer'] == $.resource.payer -%}[]{%- else -%}- payer: '{{ $.resource.payer }}'{%- endif -%}
The AWS > S3 > Bucket > Tags
control will go into alarm if there does not
exist a tag payer
with the value defined on the bucket metadata, and will
state that their should be that tag on the bucket.
Tagging Resources with Guardrails File Data
A Guardrails File is simply a json data structure that can be referenced in calculated policies. Let's assume the file has the following data:
{ "dev": "john doe", "prod": "greg duke", "qa": "gen gomes"}
The aka of this file is owner_env
. It is also possible to use the Guardrails ID,
which is of the format 220880720738517
.
We will be using the same calculated policy (AWS > S3 > Bucket > Tags > Template) as in the first example. In the query we need to not only call the resource tags but also the Guardrails file.
{ resource { turbot { tags } } tag_file: resource(id: "owner_env") { data }}
In this scenario, tag the resource with the name of the owner of the particular
environment (each environment owner is defined in the File). As an
administrator, assume we know that there can only be three values for the key
environment
: qa
, prod
, and dev
.
{%- for key, value in $.resource.turbot.tags -%}
{%- if not $.resource.turbot.tags.environment -%}- environment: "missing"{%- elif $.resource.turbot.tags.environment == 'dev' -%}- owner: {{ $.tag_file.data.dev }}{%- elif $.resource.turbot.tags.environment == 'qa' -%}- owner: {{ $.tag_file.data.qa }}{%- elif $.resource.turbot.tags.environment == 'prod' -%}- owner: {{ $.tag_file.data.prod }}{%- else -%}- environment: "invalid_value"{%- endif -%}
As always, first check for the existence of the key. If the key does exist, check it's value to see if it matches with any of the key terms, and if it does, create the necessary new tag. Lastly, if the environment key does exist but it is not qa, prod, or dev, output that the environment key has an invalid value.
Tagging Resources with Guardrails Folder Metadata
This example will detail how to pull tagging information about a Guardrails folder and apply that tag to all resources within it. The query response will include the folder that is "closest to the resource" in the Guardrails hierarchy. For example, if there existed folder A and folder B, with B inside of folder A, and a resource was inside folder B, the hierarchy would show A > B > Resource, and folder B is considered "closest."
{ resource { turbot { tags } } folder { turbot { tags } }}
Assume the tag environment
:qa
exists on the folder and this must be applied
to all resources within it. This one is easy - we can just echo the folder tag
value to set it.
{%- if $.folder.turbot.tags.environment -%}- environment: {{ $.folder.turbot.tags.environment }}{%- else -%}- environment: "folder missing tag"{%- endif -%}
If the environment tag does not exist on the folder, simply output a static value stating there is no tag.
Tagging Resources with User who Created Resource (and Creation Time)
Guardrails can automate the tagging of resources with the user who created it and the time at which it was created. Like these other examples, this will be a calculated policy, but this time our query will be a bit different!
Query:
{ resource { creator: notifications(filter: "sort: version_id limit:1") { items { actor { identity { turbot { title } } } turbot { createTimestamp } } } }}
An example response might look as follows:
{ "resource": { "creator": { "items": [ { "actor": { "identity": { "turbot": { "title": "John Doe" } } }, "turbot": { "createTimestamp": "2020-09-28T18:42:20.990Z" } } ] } }}
In the template, we simply reference the two objects then use the nunjucks template to force everything lower case, as well as replacing unapproved characters.
Template:
{%- set owner = $.resource.creator.items[0].actor.identity.turbot.title -%}created_by: "{{ owner | lower | replace(" ", "_") }}"
{%- set create_time = $.resource.creator.items[0].turbot.createTimestamp %}created_time: "{{ create_time | lower | replace(".", "_") | replace(":", "-") }}"
This policy can be applied to any resource. Be sure to test the GraphQL query to
ensure that it is responding with the correct info! Also notice that in the
second set
variable line, we remove the trailing -
to ensure that the
created_time
key is a new line and parsable by the policy.