Skip to content

Commit c59f589

Browse files
committed
add protect-against-rds-instance-deletion.sentinel
1 parent b09698f commit c59f589

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# This policy uses the Sentinel tfplan/v2 import to prevent deletion
2+
# of RDS instances that have `deletion_protection` set to true.
3+
# Note that it is unusual in using `change.actions.before` instead of
4+
# `change.actions.after`.
5+
6+
# Import common-functions/tfplan-functions/tfplan-functions.sentinel
7+
# with alias "plan"
8+
import "tfplan-functions" as plan
9+
10+
# Get all RDS instances
11+
RDSInstancesBeingDeleted = plan.find_resources_being_destroyed("aws_db_instance")
12+
13+
14+
# Filter to RDS instances with violations
15+
# Warnings will be printed for all violations since the last parameter is true
16+
violatingRDSInstances = plan.filter_attribute_was_value(RDSInstancesBeingDeleted,
17+
"deletion_protection", true, false)
18+
19+
length(violatingRDSInstances["messages"]) > 0 {
20+
print("RDS instances with deletion_protection set to true cannot be destroyed.")
21+
plan.print_violations(violatingRDSInstances["messages"], "")
22+
}
23+
24+
# Main rule
25+
main = rule {
26+
length(violatingRDSInstances["messages"]) is 0
27+
}

governance/third-generation/common-functions/tfplan-functions/tfplan-functions.sentinel

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,44 @@ filter_attribute_is_not_value = func(resources, attr, value, prtmsg) {
507507
return {"resources":violators,"messages":messages}
508508
}
509509

510+
### filter_attribute_was_not_value ###
511+
# Filter a list of resources to those with a specified
512+
# attribute (attr) that did not have a given value.
513+
# Resources should be derived by applying filters to tfplan.resource_changes.
514+
# Set prtmsg to `true` (without quotes) if you want to print violation messages.
515+
# If you want to match null, set value to "null".
516+
# Note that it this function passes `change.actions.before` instead of `rc`
517+
# to the evaluate_attribute() function which converts `rc` to `rc.change.after`.
518+
filter_attribute_was_not_value = func(resources, attr, value, prtmsg) {
519+
violators = {}
520+
messages = {}
521+
for resources as address, rc {
522+
v = evaluate_attribute(rc.change.before, attr) else null
523+
if v is null {
524+
# Add the resource and a warning message to the violators list
525+
message = to_string(address) + " had " + to_string(attr) +
526+
" that was null or undefined. " + "It was supposed to be " +
527+
to_string(value)
528+
violators[address] = rc
529+
messages[address] = message
530+
if prtmsg {
531+
print(message)
532+
}
533+
} else if v is not value {
534+
# Add the resource and a warning message to the violators list
535+
message = to_string(address) + " had " + to_string(attr) + " with value " +
536+
to_string(v) + " that was not equal to " + to_string(value)
537+
violators[address] = rc
538+
messages[address] = message
539+
if prtmsg {
540+
print(message)
541+
}
542+
}
543+
}
544+
return {"resources":violators,"messages":messages}
545+
}
546+
547+
510548
### filter_attribute_is_value ###
511549
# Filter a list of resources to those with a specified
512550
# attribute (attr) that has a given value.
@@ -535,6 +573,36 @@ filter_attribute_is_value = func(resources, attr, value, prtmsg) {
535573
return {"resources":violators,"messages":messages}
536574
}
537575

576+
### filter_attribute_was_value ###
577+
# Filter a list of resources to those with a specified
578+
# attribute (attr) that had a given value.
579+
# Resources should be derived by applying filters to tfplan.resource_changes.
580+
# Set prtmsg to `true` (without quotes) if you want to print violation messages.
581+
# If you want to match null, set value to "null".
582+
# Note that it this function passes `change.actions.before` instead of `rc`
583+
# to the evaluate_attribute() function which converts `rc` to `rc.change.after`.
584+
filter_attribute_was_value = func(resources, attr, value, prtmsg) {
585+
violators = {}
586+
messages = {}
587+
for resources as address, rc {
588+
v = evaluate_attribute(rc.change.before, attr) else null
589+
if v is null {
590+
v = "null"
591+
}
592+
if v is value {
593+
# Add the resource and a warning message to the violators list
594+
message = to_string(address) + " had " + to_string(attr) + " with value " +
595+
to_string(v) + " that was not allowed."
596+
violators[address] = rc
597+
messages[address] = message
598+
if prtmsg {
599+
print(message)
600+
}
601+
}
602+
}
603+
return {"resources":violators,"messages":messages}
604+
}
605+
538606
### filter_attribute_greater_than_value ###
539607
# Filter a list of resources to those with a specified
540608
# attribute (attr) that is greater than a given numeric value.

0 commit comments

Comments
 (0)