Skip to content

Commit f3e9cc4

Browse files
authored
Merge pull request hashicorp#273 from hashicorp/add-rds-deletion-protection-policy
Add rds deletion protection policy
2 parents b09698f + 3d770e1 commit f3e9cc4

File tree

11 files changed

+839
-4
lines changed

11 files changed

+839
-4
lines changed

governance/third-generation/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Each of these modules has several types of functions:
7272
* `resources`: a map consisting of resource changes (for tfplan/v2) or resources (for tfstate/v2) or blocks that violate a condition.
7373
* `messages`: a map of violation messages associated with the resource changes, resources, or blocks.
7474
Note that both the `resources` and `messages` collections are indexed by the address of the resources, so they will have the same order and length. The filter functions all call the `evaluate_attribute` function to evaluate attributes of resources even if nested deep within them. After calling a filter function and assigning the results to a variable like `violatingResources`, you can test if there are any violations with this condition: `length(violatingResources["messages"]) is 0`.
75-
* The `evaluate_attribute` function, which can evaluate the values of any attribute of any resource even if it is deeply nested inside the resource. It does this by calling itself recursively.
75+
* The `evaluate_attribute` function, which can evaluate the values of any attribute of any resource even if it is deeply nested inside the resource. It does this by calling itself recursively. The implementation in the tfplan-functions module will convert `rc` to `rc.change.after`. If you want it to examine previous values instead of planned values, pass it `rc.change.before` instead of `rc`.
7676
* The `to_string` function which can convert any Sentinel object to a string. It is used to build the messages in the `messages` collection returned by the filter functions.
7777
* The `print_violations` function which can be called after calling one of the filter function to print the violation messages. This would only be called if the `prtmsg` argument had been set to `false` when calling the filter function. This is sometimes desirable especially if processing blocks of resources since your policy can then print some other message that gives the address of the resource with block-level violations before printing them.
7878

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 calls the `filter_attribute_was_value()` function which passes
4+
# `rc.change.before` instead of `rc` to the `evaluate_attribute()` function
5+
# which converts `rc` to `rc.change.after`.
6+
7+
# Import common-functions/tfplan-functions/tfplan-functions.sentinel
8+
# with alias "plan"
9+
import "tfplan-functions" as plan
10+
11+
# Get all resources being destroyed
12+
resourcesBeingDestroyed = plan.find_resources_being_destroyed()
13+
14+
# Filter to RDS instances being destroyed
15+
RDSInstancesBeingDestroyed = filter resourcesBeingDestroyed as address, rc {
16+
rc.type is "aws_db_instance"
17+
}
18+
19+
# Filter to RDS instances with violations
20+
# Warnings will be printed for all violations since the last parameter is true
21+
violatingRDSInstances = plan.filter_attribute_was_value(RDSInstancesBeingDestroyed,
22+
"deletion_protection", true, false)
23+
24+
if length(violatingRDSInstances["messages"]) > 0 {
25+
print("RDS instances with deletion_protection set to true cannot be destroyed.")
26+
plan.print_violations(violatingRDSInstances["messages"], "")
27+
}
28+
29+
# Main rule
30+
main = rule {
31+
length(violatingRDSInstances["messages"]) is 0
32+
}

governance/third-generation/aws/sentinel.hcl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ policy "enforce-mandatory-tags" {
1919
enforcement_level = "advisory"
2020
}
2121

22+
policy "protect-against-rds-instance-deletion" {
23+
source = "./protect-against-rds-instance-deletion.sentinel"
24+
enforcement_level = "advisory"
25+
}
26+
2227
policy "require-dns-support-for-vpcs" {
2328
source = "./require-dns-support-for-vpcs.sentinel"
2429
enforcement_level = "advisory"
@@ -120,7 +125,7 @@ policy "restrict-sagemaker-notebooks" {
120125
}
121126

122127
policy "restrict-subnet-of-ec2-instances" {
123-
source = "./restrict-subnet-of-ec2-instancess.sentinel"
128+
source = "./restrict-subnet-of-ec2-instances.sentinel"
124129
enforcement_level = "advisory"
125130
}
126131

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module "tfplan-functions" {
2+
source = "../../../common-functions/tfplan-functions/tfplan-functions.sentinel"
3+
}
4+
5+
mock "tfplan/v2" {
6+
module {
7+
source = "mock-tfplan-fail.sentinel"
8+
}
9+
}
10+
11+
test {
12+
rules = {
13+
main = false
14+
}
15+
}
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
terraform_version = "0.13.6"
2+
3+
planned_values = {
4+
"outputs": {},
5+
"resources": {},
6+
}
7+
8+
variables = {}
9+
10+
resource_changes = {
11+
"aws_db_instance.default": {
12+
"address": "aws_db_instance.default",
13+
"change": {
14+
"actions": [
15+
"delete",
16+
],
17+
"after": null,
18+
"after_unknown": {},
19+
"before": {
20+
"address": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com",
21+
"allocated_storage": 10,
22+
"allow_major_version_upgrade": null,
23+
"apply_immediately": null,
24+
"arn": "arn:aws:rds:us-east-1:711129375688:db:terraform-20210224200525302700000001",
25+
"auto_minor_version_upgrade": true,
26+
"availability_zone": "us-east-1c",
27+
"backup_retention_period": 0,
28+
"backup_window": "06:57-07:27",
29+
"ca_cert_identifier": "rds-ca-2019",
30+
"character_set_name": null,
31+
"copy_tags_to_snapshot": false,
32+
"db_subnet_group_name": "default",
33+
"delete_automated_backups": true,
34+
"deletion_protection": true,
35+
"domain": "",
36+
"domain_iam_role_name": "",
37+
"enabled_cloudwatch_logs_exports": [],
38+
"endpoint": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com:3306",
39+
"engine": "mysql",
40+
"engine_version": "5.7.26",
41+
"final_snapshot_identifier": null,
42+
"hosted_zone_id": "Z2R2ITUGPM61AM",
43+
"iam_database_authentication_enabled": false,
44+
"id": "terraform-20210224200525302700000001",
45+
"identifier": "terraform-20210224200525302700000001",
46+
"identifier_prefix": null,
47+
"instance_class": "db.t3.micro",
48+
"iops": 0,
49+
"kms_key_id": "",
50+
"latest_restorable_time": "0001-01-01T00:00:00Z",
51+
"license_model": "general-public-license",
52+
"maintenance_window": "sat:03:23-sat:03:53",
53+
"max_allocated_storage": 0,
54+
"monitoring_interval": 0,
55+
"monitoring_role_arn": "",
56+
"multi_az": false,
57+
"name": "mydb",
58+
"option_group_name": "default:mysql-5-7",
59+
"parameter_group_name": "default.mysql5.7",
60+
"password": "foobarbaz",
61+
"performance_insights_enabled": false,
62+
"performance_insights_kms_key_id": "",
63+
"performance_insights_retention_period": 0,
64+
"port": 3306,
65+
"publicly_accessible": false,
66+
"replicas": [],
67+
"replicate_source_db": "",
68+
"resource_id": "db-FQBMWQWOZZJIQQI6U7T74XITAM",
69+
"restore_to_point_in_time": [],
70+
"s3_import": [],
71+
"security_group_names": [],
72+
"skip_final_snapshot": true,
73+
"snapshot_identifier": null,
74+
"status": "available",
75+
"storage_encrypted": false,
76+
"storage_type": "gp2",
77+
"tags": {},
78+
"timeouts": null,
79+
"timezone": "",
80+
"username": "foo",
81+
"vpc_security_group_ids": [
82+
"sg-d6ecdbf4",
83+
],
84+
},
85+
},
86+
"deposed": "",
87+
"index": null,
88+
"mode": "managed",
89+
"module_address": "",
90+
"name": "default",
91+
"provider_name": "registry.terraform.io/hashicorp/aws",
92+
"type": "aws_db_instance",
93+
},
94+
}
95+
96+
output_changes = {}
97+
98+
raw = {
99+
"configuration": {
100+
"root_module": {
101+
"resources": [
102+
{
103+
"address": "aws_db_instance.default",
104+
"expressions": {
105+
"allocated_storage": {
106+
"constant_value": 10,
107+
},
108+
"deletion_protection": {
109+
"constant_value": true,
110+
},
111+
"engine": {
112+
"constant_value": "mysql",
113+
},
114+
"engine_version": {
115+
"constant_value": "5.7",
116+
},
117+
"instance_class": {
118+
"constant_value": "db.t3.micro",
119+
},
120+
"name": {
121+
"constant_value": "mydb",
122+
},
123+
"parameter_group_name": {
124+
"constant_value": "default.mysql5.7",
125+
},
126+
"password": {
127+
"constant_value": "foobarbaz",
128+
},
129+
"skip_final_snapshot": {
130+
"constant_value": true,
131+
},
132+
"username": {
133+
"constant_value": "foo",
134+
},
135+
},
136+
"mode": "managed",
137+
"name": "default",
138+
"provider_config_key": "aws",
139+
"schema_version": 1,
140+
"type": "aws_db_instance",
141+
},
142+
],
143+
},
144+
},
145+
"format_version": "0.1",
146+
"planned_values": {
147+
"root_module": {},
148+
},
149+
"prior_state": {
150+
"format_version": "0.1",
151+
"terraform_version": "0.13.6",
152+
"values": {
153+
"root_module": {
154+
"resources": [
155+
{
156+
"address": "aws_db_instance.default",
157+
"mode": "managed",
158+
"name": "default",
159+
"provider_name": "registry.terraform.io/hashicorp/aws",
160+
"schema_version": 1,
161+
"type": "aws_db_instance",
162+
"values": {
163+
"address": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com",
164+
"allocated_storage": 10,
165+
"allow_major_version_upgrade": null,
166+
"apply_immediately": null,
167+
"arn": "arn:aws:rds:us-east-1:711129375688:db:terraform-20210224200525302700000001",
168+
"auto_minor_version_upgrade": true,
169+
"availability_zone": "us-east-1c",
170+
"backup_retention_period": 0,
171+
"backup_window": "06:57-07:27",
172+
"ca_cert_identifier": "rds-ca-2019",
173+
"character_set_name": null,
174+
"copy_tags_to_snapshot": false,
175+
"db_subnet_group_name": "default",
176+
"delete_automated_backups": true,
177+
"deletion_protection": true,
178+
"domain": "",
179+
"domain_iam_role_name": "",
180+
"enabled_cloudwatch_logs_exports": [],
181+
"endpoint": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com:3306",
182+
"engine": "mysql",
183+
"engine_version": "5.7.26",
184+
"final_snapshot_identifier": null,
185+
"hosted_zone_id": "Z2R2ITUGPM61AM",
186+
"iam_database_authentication_enabled": false,
187+
"id": "terraform-20210224200525302700000001",
188+
"identifier": "terraform-20210224200525302700000001",
189+
"identifier_prefix": null,
190+
"instance_class": "db.t3.micro",
191+
"iops": 0,
192+
"kms_key_id": "",
193+
"latest_restorable_time": "0001-01-01T00:00:00Z",
194+
"license_model": "general-public-license",
195+
"maintenance_window": "sat:03:23-sat:03:53",
196+
"max_allocated_storage": 0,
197+
"monitoring_interval": 0,
198+
"monitoring_role_arn": "",
199+
"multi_az": false,
200+
"name": "mydb",
201+
"option_group_name": "default:mysql-5-7",
202+
"parameter_group_name": "default.mysql5.7",
203+
"password": "foobarbaz",
204+
"performance_insights_enabled": false,
205+
"performance_insights_kms_key_id": "",
206+
"performance_insights_retention_period": 0,
207+
"port": 3306,
208+
"publicly_accessible": false,
209+
"replicas": [],
210+
"replicate_source_db": "",
211+
"resource_id": "db-FQBMWQWOZZJIQQI6U7T74XITAM",
212+
"restore_to_point_in_time": [],
213+
"s3_import": [],
214+
"security_group_names": [],
215+
"skip_final_snapshot": true,
216+
"snapshot_identifier": null,
217+
"status": "available",
218+
"storage_encrypted": false,
219+
"storage_type": "gp2",
220+
"tags": {},
221+
"timeouts": null,
222+
"timezone": "",
223+
"username": "foo",
224+
"vpc_security_group_ids": [
225+
"sg-d6ecdbf4",
226+
],
227+
},
228+
},
229+
],
230+
},
231+
},
232+
},
233+
"resource_changes": [
234+
{
235+
"address": "aws_db_instance.default",
236+
"change": {
237+
"actions": [
238+
"delete",
239+
],
240+
"after_unknown": {},
241+
"before": {
242+
"address": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com",
243+
"allocated_storage": 10,
244+
"allow_major_version_upgrade": null,
245+
"apply_immediately": null,
246+
"arn": "arn:aws:rds:us-east-1:711129375688:db:terraform-20210224200525302700000001",
247+
"auto_minor_version_upgrade": true,
248+
"availability_zone": "us-east-1c",
249+
"backup_retention_period": 0,
250+
"backup_window": "06:57-07:27",
251+
"ca_cert_identifier": "rds-ca-2019",
252+
"character_set_name": null,
253+
"copy_tags_to_snapshot": false,
254+
"db_subnet_group_name": "default",
255+
"delete_automated_backups": true,
256+
"deletion_protection": true,
257+
"domain": "",
258+
"domain_iam_role_name": "",
259+
"enabled_cloudwatch_logs_exports": [],
260+
"endpoint": "terraform-20210224200525302700000001.cjm7z941xa9c.us-east-1.rds.amazonaws.com:3306",
261+
"engine": "mysql",
262+
"engine_version": "5.7.26",
263+
"final_snapshot_identifier": null,
264+
"hosted_zone_id": "Z2R2ITUGPM61AM",
265+
"iam_database_authentication_enabled": false,
266+
"id": "terraform-20210224200525302700000001",
267+
"identifier": "terraform-20210224200525302700000001",
268+
"identifier_prefix": null,
269+
"instance_class": "db.t3.micro",
270+
"iops": 0,
271+
"kms_key_id": "",
272+
"latest_restorable_time": "0001-01-01T00:00:00Z",
273+
"license_model": "general-public-license",
274+
"maintenance_window": "sat:03:23-sat:03:53",
275+
"max_allocated_storage": 0,
276+
"monitoring_interval": 0,
277+
"monitoring_role_arn": "",
278+
"multi_az": false,
279+
"name": "mydb",
280+
"option_group_name": "default:mysql-5-7",
281+
"parameter_group_name": "default.mysql5.7",
282+
"password": "foobarbaz",
283+
"performance_insights_enabled": false,
284+
"performance_insights_kms_key_id": "",
285+
"performance_insights_retention_period": 0,
286+
"port": 3306,
287+
"publicly_accessible": false,
288+
"replicas": [],
289+
"replicate_source_db": "",
290+
"resource_id": "db-FQBMWQWOZZJIQQI6U7T74XITAM",
291+
"restore_to_point_in_time": [],
292+
"s3_import": [],
293+
"security_group_names": [],
294+
"skip_final_snapshot": true,
295+
"snapshot_identifier": null,
296+
"status": "available",
297+
"storage_encrypted": false,
298+
"storage_type": "gp2",
299+
"tags": {},
300+
"timeouts": null,
301+
"timezone": "",
302+
"username": "foo",
303+
"vpc_security_group_ids": [
304+
"sg-d6ecdbf4",
305+
],
306+
},
307+
},
308+
"mode": "managed",
309+
"name": "default",
310+
"provider_name": "registry.terraform.io/hashicorp/aws",
311+
"type": "aws_db_instance",
312+
},
313+
],
314+
"terraform_version": "0.13.6",
315+
}

0 commit comments

Comments
 (0)