Skip to content

Commit 18dae9b

Browse files
authored
Merge pull request hashicorp#158 from hashicorp/add-prevent-resource-destruction-policy
add prevent destruction policy and function
2 parents 3553039 + 96819de commit 18dae9b

File tree

11 files changed

+1693
-0
lines changed

11 files changed

+1693
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# This policy prevents any destruction of resources of blacklisted types.
2+
3+
##### Imports #####
4+
5+
import "tfplan"
6+
import "strings"
7+
8+
##### Functions #####
9+
10+
# Find all resources of a specific type from all modules using the tfplan import
11+
find_resources_from_plan = func(type) {
12+
13+
resources = {}
14+
15+
# Iterate over all modules in the tfplan import
16+
for tfplan.module_paths as path {
17+
# Iterate over the named resources of desired type in the module
18+
for tfplan.module(path).resources[type] else {} as name, instances {
19+
# Iterate over resource instances
20+
for instances as index, r {
21+
22+
# Get the address of the instance
23+
if length(path) == 0 {
24+
# root module
25+
address = type + "." + name + "[" + string(index) + "]"
26+
} else {
27+
# non-root module
28+
address = "module." + strings.join(path, ".module.") + "." +
29+
type + "." + name + "[" + string(index) + "]"
30+
}
31+
32+
# Add the instance to resources map, setting the key to the address
33+
resources[address] = r
34+
}
35+
}
36+
}
37+
38+
return resources
39+
}
40+
41+
# Validate that resources of blacklisted types are not being destroyed
42+
validate_destroyed_resources = func(blacklist) {
43+
44+
valid = true
45+
46+
for blacklist as type {
47+
found_resources = find_resources_from_plan(type)
48+
for found_resources as address, r {
49+
if r.destroy and not r.requires_new {
50+
print("You are trying to destroy a resource", address,
51+
"of blacklisted type", type)
52+
valid = false
53+
}
54+
}
55+
}
56+
57+
return valid
58+
}
59+
60+
##### Lists #####
61+
62+
# List of blacklisted resources
63+
blacklist = [
64+
"aws_vpc",
65+
"azurerm_virtual_network",
66+
"google_compute_network",
67+
]
68+
69+
##### Rules #####
70+
71+
# Main rule
72+
destroy_validated = validate_destroyed_resources(blacklist)
73+
main = rule {
74+
destroy_validated
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mock": {
3+
"tfplan": "mock-tfplan-fail-0.11.sentinel"
4+
},
5+
"test": {
6+
"main": false
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mock": {
3+
"tfplan": "mock-tfplan-fail-0.12.sentinel"
4+
},
5+
"test": {
6+
"main": false
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import "strings"
2+
import "types"
3+
4+
_modules = {
5+
"root": {
6+
"data": {},
7+
"path": [],
8+
"resources": {
9+
"azurerm_network_interface": {
10+
"main": {
11+
0: {
12+
"destroy": true,
13+
"diff": {},
14+
"requires_new": false,
15+
},
16+
},
17+
},
18+
"azurerm_resource_group": {
19+
"main": {
20+
0: {
21+
"destroy": true,
22+
"diff": {},
23+
"requires_new": false,
24+
},
25+
},
26+
},
27+
"azurerm_subnet": {
28+
"internal": {
29+
0: {
30+
"destroy": true,
31+
"diff": {},
32+
"requires_new": false,
33+
},
34+
},
35+
},
36+
"azurerm_virtual_machine": {
37+
"demo": {
38+
0: {
39+
"destroy": true,
40+
"diff": {},
41+
"requires_new": false,
42+
},
43+
},
44+
},
45+
"azurerm_virtual_network": {
46+
"main": {
47+
0: {
48+
"destroy": true,
49+
"diff": {},
50+
"requires_new": false,
51+
},
52+
},
53+
},
54+
},
55+
},
56+
}
57+
58+
module_paths = [
59+
[],
60+
]
61+
62+
terraform_version = "0.11.14"
63+
64+
variables = {
65+
"prefix": "azure-demo",
66+
"vm_size": "Standard_A1",
67+
}
68+
69+
module = func(path) {
70+
if types.type_of(path) is not "list" {
71+
error("expected list, got", types.type_of(path))
72+
}
73+
74+
if length(path) < 1 {
75+
return _modules.root
76+
}
77+
78+
addr = []
79+
for path as p {
80+
append(addr, "module")
81+
append(addr, p)
82+
}
83+
84+
return _modules[strings.join(addr, ".")]
85+
}
86+
87+
data = _modules.root.data
88+
path = _modules.root.path
89+
resources = _modules.root.resources

0 commit comments

Comments
 (0)