Skip to content

Commit ba1c196

Browse files
authored
Merge pull request hashicorp#259 from hashicorp/add-azure-functions
update enforce-mandatory-tags policies
2 parents dbb970e + dbbc143 commit ba1c196

File tree

9 files changed

+128
-41
lines changed

9 files changed

+128
-41
lines changed

governance/third-generation/README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ You can find most of the common functions used in the third-generation policies
3333
* [tfconfig-functions](./common-functions/tfconfig-functions)
3434
* [tfrun-functions](./common-functions/tfrun-functions)
3535

36-
There are also some functions used to validate assumed roles for the AWS provider in [aws-functions](./aws/aws-functions).
36+
There are also some functions that can be used with the AWS and Azure providers in [aws-functions](./aws/aws-functions) and [azure-functions](./azure/azure-functions).
3737

3838
Unlike the second-generation common functions that were each defined in a separate file, all of the common functions that use any of the 4 Terraform Sentinel imports (tfplan/v2, tfstate/v2, tfconfig/v2, and tfrun) are defined in a single file. This makes it easier to import all of the functions that use one of those imports into the Sentinel CLI test cases and Terraform Cloud policy sets, since those only need a single stanza such as this one for each module:
3939
```
@@ -43,7 +43,7 @@ Unlike the second-generation common functions that were each defined in a separa
4343
}
4444
}
4545
```
46-
Test cases that use the other modules would either change all three occurrences of "tfplan" in that stanza to "tfstate", "tfconfig", or "tfrun" or would add additional stanzas with those changes.
46+
Test cases that use the other modules would either change all three occurrences of "tfplan" in that stanza to "tfstate", "tfconfig", "tfrun", "aws", or "azure" or would add additional stanzas with those changes.
4747

4848
We have put each Sentinel module in its own directory which also contains Markdown files for each of the module's functions under a docs directory. Each of these Markdown files describes the function, its declaration, its arguments, other common functions it uses, what it returns, and what it prints. It also gives examples of calling the function and sometimes lists some policies that call it.
4949

@@ -56,8 +56,9 @@ import "tfstate-functions" as state
5656
import "tfconfig-functions" as config
5757
import "tfrun-functions" as run
5858
import "aws-functions" as aws
59+
import "azure-functions" as azure
5960
```
60-
In this case, we are using `plan`, `state`, `config`, `run`, and `aws` as aliases for the five imports to keep lines that use their functions shorter. Of course, you only need to import the modules that contain functions that your policy actually calls.
61+
In this case, we are using `plan`, `state`, `config`, `run`, `aws`, and `azure` as aliases for the six imports to keep lines that use their functions shorter. Of course, you only need to import the modules that contain functions that your policy actually calls.
6162

6263
### The Functions of the tfplan-functions and tfstate-functions Modules
6364
We discuss these two modules together because they are essentially identical except for their use of the tfplan/v2 and tfstate/v2 imports.
@@ -106,7 +107,7 @@ Documentation for each individual function can be found in this directory:
106107

107108
### The Functions of the aws-functions Module
108109
The `aws-functions` module (which is located under in the aws/aws-functions directory) has the following functions:
109-
* The `find_resources_with_standard_tags` function finds all AWS resources that use standard AWS tags in the current plan that are being created or modified.
110+
* The `find_resources_with_standard_tags` function finds all AWS resources of specified types that should have tags in the current plan that are not being permanently deleted.
110111
* The `determine_role_arn` function determines the ARN of a role set in the `role_arn` parameter of an AWS provider. It can only determine the role_arn if it is set to either a hard-coded value or to a reference to a single Terraform variable. It sets the role to "complex" if it finds a single non-variable reference or if it finds multiple references. It sets the role to "none" if no role arn is found.
111112
* The `get_assumed_roles` function gets all roles assumed by AWS providers in the current Terraform configuration. It calls the `determine_role_arn` function.
112113
* The `validate_assumed_roles_with_list` function validates assumed roles found by the `get_assumed_roles` function against a list of role ARNs.
@@ -115,6 +116,13 @@ The `aws-functions` module (which is located under in the aws/aws-functions dire
115116
Documentation for each individual function can be found in this directory:
116117
* [aws-functions](./aws/aws-functions/docs)
117118

119+
### The Functions of the azure-functions Module
120+
The `azure-functions` module (which is located under in the azure/azure-functions directory) has the following functions:
121+
* The `find_resources_with_standard_tags` function finds all Azure resources of specified types that should have tags in the current plan that are not being permanently deleted.
122+
123+
Documentation for each individual function can be found in this directory:
124+
* [azure-functions](./azure/azure-functions/docs)
125+
118126
## Mock Files and Test Cases
119127
Sentinel [mock files](https://www.terraform.io/docs/enterprise/sentinel/mock.html) and [test cases](https://docs.hashicorp.com/sentinel/commands/config#test-cases) have been provided under the test directory of each cloud so that all the policies can be tested with the [Sentinel CLI](https://docs.hashicorp.com/sentinel/commands). The mocks were generated from actual Terraform 0.12 plans run against Terraform code that provisioned resources in these clouds. The pass and fail mock files were edited to respectively pass and fail the associated Sentinel policies. Some policies, including those that have multiple rules, have multiple fail mock files with names that indicate which condition or conditions they fail.
120128

governance/third-generation/aws/aws-functions/aws-functions.sentinel

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import "types"
1010
##### Functions #####
1111

1212
### find_resources_with_standard_tags ###
13-
find_resources_with_standard_tags = func() {
13+
find_resources_with_standard_tags = func(resource_types) {
1414
resources = filter tfplan.resource_changes as address, rc {
1515
rc.provider_name matches "(.*)aws$" and
16-
rc.type not in ["aws_autoscaling_group"] and
16+
rc.type in resource_types and
1717
rc.mode is "managed" and
18-
(rc.change.actions contains "create" or rc.change.actions contains "update")
18+
(rc.change.actions contains "create" or rc.change.actions contains "update" or
19+
rc.change.actions contains "read" or rc.change.actions contains "no-op")
1920
}
2021

2122
return resources
Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
# find_resources_with_standard_tags
2-
This function finds all resource instances for the AWS provider that use standard AWS tags in the current plan that are being created or modified using the [tfplan/v2](https://www.terraform.io/docs/cloud/sentinel/import/tfplan-v2.html) import.
2+
This function finds all AWS resource instances of specified types in the current plan that are not being permanently deleted using the [tfplan/v2](https://www.terraform.io/docs/cloud/sentinel/import/tfplan-v2.html) import.
33

4-
Currently, the only AWS resource excluded is `aws_autoscaling_group`. If you discover other AWS resources that do not use the `tags` attribute in the standard way, then add them to the list that already includes `aws_autoscaling_group`.
4+
It was updated on 9/29/2020 to work with both the short name of the AWS provider, "aws", and fully-qualfied provider names that match the regex, `(.*)aws$`. This was required because Terraform 0.13 and above returns the fully-qualified names of providers such as "registry.terraform.io/hashicorp/aws" to Sentinel. Older versions of Terraform only return the short-form such as "aws".
55

6-
It was updated on 9/29/2020 to work with both the short name of the AWS provider, "aws", and fully-qualfied provider names that match the regex, `(.*)aws$`. This was required because Terraform 0.13 returns the fully-qualified names of providers such as "registry.terraform.io/hashicorp/aws" to Sentinel. Older versions of Terraform only return the short-form such as "aws".
6+
It was updated on 2/8/2021 to only look for tags in a given list of AWS
7+
resources instead of all AWS resources except those in a given list. We made
8+
this change because we discovered that there are many AWS resources that do
9+
not include standard AWS tags.
710

811
## Sentinel Module
912
This function is contained in the [aws-functions.sentinel](../aws-functions.sentinel) module.
1013

1114
## Declaration
12-
`find_resources_with_standard_tags = func()`
15+
`find_resources_with_standard_tags = func(resource_types)`
1316

1417
## Arguments
15-
* None
18+
* **resource_types**: a list of AWS resource types that should have specified tags defined.
1619

1720
## Common Functions Used
1821
None
@@ -26,7 +29,13 @@ This function does not print anything.
2629
## Examples
2730
Here is an example of calling this function, assuming that the aws-functions.sentinel file that contains it has been imported with the alias `aws`:
2831
```
29-
allAWSResourcesWithStandardTags = aws.find_resources_with_standard_tags()
32+
resource_types = [
33+
"aws_s3_bucket",
34+
"aws_instance",
35+
]
36+
37+
allAWSResourcesWithStandardTags =
38+
aws.find_resources_with_standard_tags(resource_types)
3039
```
3140

3241
This function is used by the [enforce-mandatory-tags.sentinel](../../enforce-mandatory-tags.sentinel) policy.

governance/third-generation/aws/enforce-mandatory-tags.sentinel

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This policy uses the Sentinel tfplan/v2 import to require that
2-
# all AWS resources that use standard AWS tags have all mandatory tags
2+
# specified AWS resources have all mandatory tags
33

44
# Import common-functions/tfplan-functions/tfplan-functions.sentinel
55
# with alias "plan"
@@ -9,18 +9,26 @@ import "tfplan-functions" as plan
99
# with alias "aws"
1010
import "aws-functions" as aws
1111

12+
# List of resources that are required to have name/value tags
13+
param resource_types default [
14+
"aws_s3_bucket",
15+
"aws_instance",
16+
]
17+
1218
# List of mandatory tags
1319
# Note that the tags here are for internal HashiCorp usage
1420
# You should assign your own tags in a "mandatory_tags" parameter in your policy set
1521
# Or change the tags here in the policy.
1622
param mandatory_tags default ["Name", "ttl", "owner", "se-region", "purpose", "terraform"]
1723

1824
# Get all AWS Resources with standard tags
19-
allAWSResourcesWithStandardTags = aws.find_resources_with_standard_tags()
25+
allAWSResourcesWithStandardTags =
26+
aws.find_resources_with_standard_tags(resource_types)
2027

2128
# Filter to AWS resources with violations
2229
# Warnings will be printed for all violations since the last parameter is true
23-
violatingAWSResources = plan.filter_attribute_not_contains_list(allAWSResourcesWithStandardTags,
30+
violatingAWSResources =
31+
plan.filter_attribute_not_contains_list(allAWSResourcesWithStandardTags,
2432
"tags", mandatory_tags, true)
2533

2634
# Main rule
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Common functions for use with the Azure provider
2+
3+
##### Imports #####
4+
import "tfplan/v2" as tfplan
5+
6+
##### Functions #####
7+
8+
### find_resources_with_standard_tags ###
9+
find_resources_with_standard_tags = func(resource_types) {
10+
resources = filter tfplan.resource_changes as address, rc {
11+
rc.provider_name matches "(.*)azurerm$" and
12+
rc.type in resource_types and
13+
rc.mode is "managed" and
14+
(rc.change.actions contains "create" or rc.change.actions contains "update" or
15+
rc.change.actions contains "read" or rc.change.actions contains "no-op")
16+
}
17+
18+
return resources
19+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# find_resources_with_standard_tags
2+
This function finds all Azure resource instances of specified types in the current plan that are not being permanently deleted using the [tfplan/v2](https://www.terraform.io/docs/cloud/sentinel/import/tfplan-v2.html) import.
3+
4+
This function works with both the short name of the Azure provider, "azurerm", and fully-qualfied provider names that match the regex, `(.*)azurerm$`. The latter is required because Terraform 0.13 and above returns the fully-qualified names of providers such as "registry.terraform.io/hashicorp/azurerm" to Sentinel. Older versions of Terraform only return the short-form such as "azurerm".
5+
6+
## Sentinel Module
7+
This function is contained in the [azure-functions.sentinel](../azure-functions.sentinel) module.
8+
9+
## Declaration
10+
`find_resources_with_standard_tags = func(resource_types)`
11+
12+
## Arguments
13+
* **resource_types**: a list of Azure resource types that should have specified tags defined.
14+
15+
## Common Functions Used
16+
None
17+
18+
## What It Returns
19+
This function returns a single flat map of resource instances indexed by the complete [addresses](https://www.terraform.io/docs/internals/resource-addressing.html) of the instances. The map is actually a filtered sub-collection of the [`tfplan.resource_changes`](https://www.terraform.io/docs/cloud/sentinel/import/tfplan-v2.html#the-resource_changes-collection) collection.
20+
21+
## What It Prints
22+
This function does not print anything.
23+
24+
## Examples
25+
Here is an example of calling this function, assuming that the aws-functions.sentinel file that contains it has been imported with the alias `azure`:
26+
```
27+
resource_types = [
28+
"azurerm_resource_group",
29+
"azurerm_virtual_machine"
30+
"azurerm_linux_virtual_machine",
31+
"azurerm_windows_virtual_machine",
32+
]
33+
34+
allAzureSResourcesWithStandardTags =
35+
azure.find_resources_with_standard_tags(resource_types)
36+
```
37+
38+
This function is used by the [enforce-mandatory-tags.sentinel](../../enforce-mandatory-tags.sentinel) policy.
Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,38 @@
11
# This policy uses the Sentinel tfplan/v2 import to require that
2-
# all Azure VMs have all mandatory tags
2+
# specified Azure resources have all mandatory tags
33

44
# Import common-functions/tfplan-functions/tfplan-functions.sentinel
55
# with alias "plan"
66
import "tfplan-functions" as plan
77

8-
# List of mandatory tags
9-
mandatory_tags = ["environment"]
8+
# Import azure-functions/azure-functions.sentinel
9+
# with alias "azure"
10+
import "azure-functions" as azure
1011

11-
# Get all Azure resources using azurerm_virtual_machine
12-
allAzureVMs = plan.find_resources("azurerm_virtual_machine")
12+
# List of Azure resources that are required to have name/value tags.
13+
param resource_types default [
14+
"azurerm_resource_group",
15+
"azurerm_virtual_machine",
16+
"azurerm_linux_virtual_machine",
17+
"azurerm_windows_virtual_machine",
18+
"azurerm_virtual_network",
19+
]
1320

14-
# Filter to Azure resources with violations using azurerm_virtual_machine
15-
# Warnings will be printed for all violations since the last parameter is true
16-
violatingAzureVMs = plan.filter_attribute_not_contains_list(allAzureVMs,
17-
"tags", mandatory_tags, true)
21+
# List of mandatory tags
22+
param mandatory_tags default ["environment"]
1823

19-
# Get all Azure VMs using azurerm_windows_virtual_machine
20-
allAzureWindowsVMs = plan.find_resources("azurerm_windows_virtual_machine")
24+
# Get all Azure Resources with standard tags
25+
allAzureResourcesWithStandardTags =
26+
azure.find_resources_with_standard_tags(resource_types)
2127

22-
# Filter to Azure VMs with violations that use azurerm_windows_virtual_machine
28+
# Filter to Azure resources with violations using azurerm_virtual_machine
2329
# Warnings will be printed for all violations since the last parameter is true
24-
violatingAzureWindowsVMs = plan.filter_attribute_not_contains_list(allAzureWindowsVMs,
30+
violatingAzureResources =
31+
plan.filter_attribute_not_contains_list(allAzureResourcesWithStandardTags,
2532
"tags", mandatory_tags, true)
2633

27-
# Get all Azure VMs using azurerm_linux_virtual_machine
28-
allAzureLinuxVMs = plan.find_resources("azurerm_linux_virtual_machine")
29-
30-
# Filter to Azure VMs with violations that use azurerm_linux_virtual_machine
31-
# Warnings will be printed for all violations since the last parameter is true
32-
violatingAzureLinuxVMs = plan.filter_attribute_not_contains_list(allAzureLinuxVMs,
33-
"tags", mandatory_tags, true)
3434

3535
# Main rule
36-
violations = length(violatingAzureVMs["messages"]) +
37-
length(violatingAzureWindowsVMs["messages"]) +
38-
length(violatingAzureLinuxVMs["messages"])
39-
4036
main = rule {
41-
violations is 0
37+
length(violatingAzureResources["messages"]) is 0
4238
}

governance/third-generation/azure/test/enforce-mandatory-tags/fail.hcl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
module "azure-functions" {
2+
source = "../../azure-functions/azure-functions.sentinel"
3+
}
4+
15
module "tfplan-functions" {
26
source = "../../../common-functions/tfplan-functions/tfplan-functions.sentinel"
37
}

governance/third-generation/azure/test/enforce-mandatory-tags/pass.hcl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
module "azure-functions" {
2+
source = "../../azure-functions/azure-functions.sentinel"
3+
}
4+
15
module "tfplan-functions" {
26
source = "../../../common-functions/tfplan-functions/tfplan-functions.sentinel"
37
}

0 commit comments

Comments
 (0)