Skip to content

Commit 77d39ac

Browse files
author
Sean Carolan
committed
Add initial policy for instruqt track.
0 parents  commit 77d39ac

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Terraform Cloud Workshop Sentinel Policies
2+
This repository contains sample sentinel policies for use in Terraform Cloud workshops.
3+
4+
Use with the curriculum available here:
5+
6+
https://hashicorp.github.io/workshops

aws/enforce-mandatory-tags.sentinel

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# This policy uses the Sentinel tfplan import to require that all EC2 instances
2+
# have all mandatory tags.
3+
4+
# Note that the comparison is case-sensitive since AWS tags are case-sensitive.
5+
6+
##### Imports #####
7+
8+
import "tfplan"
9+
import "strings"
10+
import "types"
11+
12+
##### Functions #####
13+
14+
# Find all resources of a specific type from all modules using the tfplan import
15+
find_resources_from_plan = func(type) {
16+
17+
resources = {}
18+
19+
# Iterate over all modules in the tfplan import
20+
for tfplan.module_paths as path {
21+
# Iterate over the named resources of desired type in the module
22+
for tfplan.module(path).resources[type] else {} as name, instances {
23+
# Iterate over resource instances
24+
for instances as index, r {
25+
26+
# Get the address of the instance
27+
if length(path) == 0 {
28+
# root module
29+
address = type + "." + name + "[" + string(index) + "]"
30+
} else {
31+
# non-root module
32+
address = "module." + strings.join(path, ".module.") + "." +
33+
type + "." + name + "[" + string(index) + "]"
34+
}
35+
36+
# Add the instance to resources map, setting the key to the address
37+
resources[address] = r
38+
}
39+
}
40+
}
41+
42+
return resources
43+
}
44+
45+
# Validate that all instances of specified type have a specified top-level
46+
# attribute that contains all members of a given list
47+
validate_attribute_contains_list = func(type, attribute, required_values) {
48+
49+
validated = true
50+
51+
# Get all resource instances of the specified type
52+
resource_instances = find_resources_from_plan(type)
53+
54+
# Loop through the resource instances
55+
for resource_instances as address, r {
56+
57+
# Skip resource instances that are being destroyed
58+
# to avoid unnecessary policy violations.
59+
# Used to be: if length(r.diff) == 0
60+
if r.destroy and not r.requires_new {
61+
print("Skipping resource", address, "that is being destroyed.")
62+
continue
63+
}
64+
65+
# Determine if the attribute is computed
66+
# We check "attribute.%" and "attribute.#" because an
67+
# attribute of type map or list won't show up in the diff
68+
if (r.diff[attribute + ".%"].computed else false) or
69+
(r.diff[attribute + ".#"].computed else false) {
70+
print("Resource", address, "has attribute", attribute,
71+
"that is computed.")
72+
# If you want computed values to cause the policy to fail,
73+
# uncomment the next line.
74+
# validated = false
75+
} else {
76+
# Validate that the attribute is a list or a map
77+
# but first check if r.applied[attribute] exists
78+
if r.applied[attribute] else null is not null and
79+
(types.type_of(r.applied[attribute]) is "list" or
80+
types.type_of(r.applied[attribute]) is "map") {
81+
82+
# Evaluate each member of required_values list
83+
for required_values as rv {
84+
if r.applied[attribute] not contains rv {
85+
print("Resource", address, "has attribute", attribute,
86+
"that is missing required value", rv, "from the list:",
87+
required_values)
88+
validated = false
89+
} // end rv
90+
} // end required_values
91+
92+
} else {
93+
print("Resource", address, "is missing attribute", attribute,
94+
"or it is not a list or a map")
95+
validated = false
96+
} // end check that attribute is list or map
97+
98+
} // end computed check
99+
} // end resource instances
100+
101+
return validated
102+
}
103+
104+
### List of mandatory tags ###
105+
mandatory_tags = [
106+
"Department",
107+
"Billable",
108+
]
109+
110+
### Rules ###
111+
112+
# Call the validation function
113+
tags_validated = validate_attribute_contains_list("aws_instance",
114+
"tags", mandatory_tags)
115+
116+
#Main rule
117+
main = rule {
118+
tags_validated
119+
}

aws/sentinel.hcl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
policy "enforce-mandatory-tags" {
2+
enforcement_level = "advisory"
3+
}

0 commit comments

Comments
 (0)